Avi Drissman | 914a636c | 2022-09-27 20:08:53 | [diff] [blame] | 1 | # Copyright 2017 The Chromium Authors |
damargulis | 5c5337d | 2017-04-21 22:36:08 | [diff] [blame] | 2 | # Use of this source code is governed by a BSD-style license that can be |
| 3 | # found in the LICENSE file. |
| 4 | """Used by a js_binary action to compile javascript files. |
| 5 | |
| 6 | This script takes in a list of sources and dependencies and compiles them all |
| 7 | together into a single compiled .js file. The dependencies are ordered in a |
| 8 | post-order, left-to-right traversal order. If multiple instances of the same |
| 9 | source file are read, only the first is kept. The script can also take in |
| 10 | optional --flags argument which will add custom flags to the compiler. Any |
| 11 | extern files can also be passed in using the --extern flag. |
| 12 | """ |
| 13 | |
Luciano Pacheco | aab13d41 | 2021-01-06 08:58:18 | [diff] [blame] | 14 | from __future__ import print_function |
| 15 | |
Mike Bjorge | 61bf88e | 2017-05-14 07:34:06 | [diff] [blame] | 16 | import argparse |
damargulis | 5c5337d | 2017-04-21 22:36:08 | [diff] [blame] | 17 | import os |
Mike Bjorge | 61bf88e | 2017-05-14 07:34:06 | [diff] [blame] | 18 | import sys |
| 19 | |
Dan Beam | 0135f0c | 2019-01-23 02:18:37 | [diff] [blame] | 20 | import compiler |
damargulis | 5c5337d | 2017-04-21 22:36:08 | [diff] [blame] | 21 | |
| 22 | |
| 23 | def ParseDepList(dep): |
Christopher Lam | 997fd81 | 2017-11-03 03:42:40 | [diff] [blame] | 24 | """Parses a dependency list, returns |sources, deps, externs|.""" |
| 25 | assert os.path.isfile(dep), (dep + |
damargulis | 5c5337d | 2017-04-21 22:36:08 | [diff] [blame] | 26 | ' is not a js_library target') |
| 27 | with open(dep, 'r') as dep_list: |
| 28 | lines = dep_list.read().splitlines() |
| 29 | assert 'deps:' in lines, dep + ' is not formated correctly, missing "deps:"' |
Christopher Lam | 997fd81 | 2017-11-03 03:42:40 | [diff] [blame] | 30 | deps_start = lines.index('deps:') |
| 31 | assert 'externs:' in lines, dep + ' is not formated correctly, missing "externs:"' |
| 32 | externs_start = lines.index('externs:') |
| 33 | |
| 34 | return (lines[1:deps_start], |
| 35 | lines[deps_start+1:externs_start], |
| 36 | lines[externs_start+1:]) |
damargulis | 5c5337d | 2017-04-21 22:36:08 | [diff] [blame] | 37 | |
| 38 | |
Trent Apted | 131641f0 | 2018-08-09 23:46:45 | [diff] [blame] | 39 | # Cache, to avoid reading the same file twice in the dependency tree and |
| 40 | # processing its dependencies again. |
| 41 | depcache = {} |
| 42 | |
| 43 | def AppendUnique(items, new_items): |
| 44 | """Append items in |new_items| to |items|, avoiding duplicates.""" |
| 45 | # Note this is O(n*n), and assumes |new_items| is already unique, but this is |
| 46 | # not a bottleneck overall. |
| 47 | items += [i for i in new_items if i not in items] |
| 48 | |
| 49 | def CrawlDepsTree(deps): |
damargulis | 5c5337d | 2017-04-21 22:36:08 | [diff] [blame] | 50 | """Parses the dependency tree creating a post-order listing of sources.""" |
Trent Apted | 131641f0 | 2018-08-09 23:46:45 | [diff] [blame] | 51 | global depcache |
| 52 | |
| 53 | if len(deps) == 0: |
| 54 | return ([], []) |
| 55 | |
| 56 | new_sources = [] |
| 57 | new_externs = [] |
damargulis | 5c5337d | 2017-04-21 22:36:08 | [diff] [blame] | 58 | for dep in deps: |
Trent Apted | 131641f0 | 2018-08-09 23:46:45 | [diff] [blame] | 59 | if dep in depcache: |
| 60 | cur_sources, cur_externs = depcache[dep] |
| 61 | else: |
| 62 | dep_sources, dep_deps, dep_externs = ParseDepList(dep) |
| 63 | cur_sources, cur_externs = CrawlDepsTree(dep_deps) |
| 64 | # Add child dependencies of this node before the current node, then cache. |
| 65 | AppendUnique(cur_sources, dep_sources) |
| 66 | AppendUnique(cur_externs, dep_externs) |
| 67 | depcache[dep] = (cur_sources, cur_externs) |
Christopher Lam | 997fd81 | 2017-11-03 03:42:40 | [diff] [blame] | 68 | |
| 69 | # Add the current node's sources and dedupe. |
Trent Apted | 131641f0 | 2018-08-09 23:46:45 | [diff] [blame] | 70 | AppendUnique(new_sources, cur_sources) |
| 71 | AppendUnique(new_externs, cur_externs) |
Christopher Lam | 997fd81 | 2017-11-03 03:42:40 | [diff] [blame] | 72 | |
Trent Apted | 131641f0 | 2018-08-09 23:46:45 | [diff] [blame] | 73 | return new_sources, new_externs |
Christopher Lam | 997fd81 | 2017-11-03 03:42:40 | [diff] [blame] | 74 | |
Trent Apted | 131641f0 | 2018-08-09 23:46:45 | [diff] [blame] | 75 | |
| 76 | def CrawlRootDepsTree(deps, target_sources, target_externs): |
| 77 | """Parses the dependency tree and adds target sources.""" |
| 78 | sources, externs = CrawlDepsTree(deps) |
| 79 | AppendUnique(sources, target_sources) |
| 80 | AppendUnique(externs, target_externs) |
Christopher Lam | 997fd81 | 2017-11-03 03:42:40 | [diff] [blame] | 81 | return sources, externs |
damargulis | 5c5337d | 2017-04-21 22:36:08 | [diff] [blame] | 82 | |
| 83 | |
| 84 | def main(): |
Mike Bjorge | 61bf88e | 2017-05-14 07:34:06 | [diff] [blame] | 85 | parser = argparse.ArgumentParser() |
damargulis | 5c5337d | 2017-04-21 22:36:08 | [diff] [blame] | 86 | parser.add_argument('-c', '--compiler', required=True, |
| 87 | help='Path to compiler') |
| 88 | parser.add_argument('-s', '--sources', nargs='*', default=[], |
| 89 | help='List of js source files') |
Shawn Quereshi | 7eeef23 | 2021-10-19 20:39:26 | [diff] [blame] | 90 | parser.add_argument('-o', '--output', required=False, |
damargulis | 5c5337d | 2017-04-21 22:36:08 | [diff] [blame] | 91 | help='Compile to output') |
Shawn Quereshi | 7eeef23 | 2021-10-19 20:39:26 | [diff] [blame] | 92 | parser.add_argument('--chunks', action='store_true', |
| 93 | help='Compile each source to its own chunk') |
| 94 | parser.add_argument('--chunk_suffix', required=False, |
| 95 | help='String appended to the source when naming a chunk') |
damargulis | 5c5337d | 2017-04-21 22:36:08 | [diff] [blame] | 96 | parser.add_argument('-d', '--deps', nargs='*', default=[], |
| 97 | help='List of js_libarary dependencies') |
| 98 | parser.add_argument('-b', '--bootstrap', |
| 99 | help='A file to include before all others') |
| 100 | parser.add_argument('-cf', '--config', nargs='*', default=[], |
| 101 | help='A list of files to include after bootstrap and ' |
| 102 | 'before all others') |
| 103 | parser.add_argument('-f', '--flags', nargs='*', default=[], |
| 104 | help='A list of custom flags to pass to the compiler. ' |
| 105 | 'Do not include leading dashes') |
| 106 | parser.add_argument('-e', '--externs', nargs='*', default=[], |
| 107 | help='A list of extern files to pass to the compiler') |
Christopher Lam | c756674 | 2018-07-18 03:02:06 | [diff] [blame] | 108 | parser.add_argument('-co', '--checks-only', action='store_true', |
| 109 | help='Only performs checks and writes an empty output') |
damargulis | 5c5337d | 2017-04-21 22:36:08 | [diff] [blame] | 110 | |
| 111 | args = parser.parse_args() |
damargulis | 5c5337d | 2017-04-21 22:36:08 | [diff] [blame] | 112 | |
Shawn Quereshi | 7eeef23 | 2021-10-19 20:39:26 | [diff] [blame] | 113 | # If --chunks is used, args.sources will be added later |
| 114 | sources, externs = CrawlRootDepsTree(args.deps, [] if args.chunks else args.sources, args.externs) |
damargulis | 5c5337d | 2017-04-21 22:36:08 | [diff] [blame] | 115 | compiler_args = ['--%s' % flag for flag in args.flags] |
Trent Apted | 131641f0 | 2018-08-09 23:46:45 | [diff] [blame] | 116 | compiler_args += ['--externs=%s' % e for e in externs] |
Shawn Quereshi | 7eeef23 | 2021-10-19 20:39:26 | [diff] [blame] | 117 | |
| 118 | if not args.chunks: |
| 119 | compiler_args += [ |
| 120 | '--js_output_file', |
| 121 | args.output, |
| 122 | ] |
| 123 | |
damargulis | 5c5337d | 2017-04-21 22:36:08 | [diff] [blame] | 124 | compiler_args += [ |
damargulis | 5c5337d | 2017-04-21 22:36:08 | [diff] [blame] | 125 | '--js', |
| 126 | ] |
Shawn Quereshi | 7eeef23 | 2021-10-19 20:39:26 | [diff] [blame] | 127 | |
damargulis | 5c5337d | 2017-04-21 22:36:08 | [diff] [blame] | 128 | if args.bootstrap: |
| 129 | compiler_args += [args.bootstrap] |
Shawn Quereshi | 7eeef23 | 2021-10-19 20:39:26 | [diff] [blame] | 130 | |
damargulis | 5c5337d | 2017-04-21 22:36:08 | [diff] [blame] | 131 | compiler_args += args.config |
| 132 | compiler_args += sources |
| 133 | |
Shawn Quereshi | 7eeef23 | 2021-10-19 20:39:26 | [diff] [blame] | 134 | if args.chunks: |
| 135 | chunk_suffix = args.chunk_suffix |
| 136 | common_chunk_name = 'common' + chunk_suffix |
| 137 | compiler_args += [ |
| 138 | '--chunk_output_path_prefix {}'.format(args.output), |
| 139 | '--chunk {}:auto'.format(common_chunk_name) |
| 140 | ] |
| 141 | |
| 142 | for s in args.sources: |
| 143 | # '//path/to/target.js' becomes 'target' |
| 144 | chunk_name = '{}{}'.format(s.split('/')[-1].split('.')[0], chunk_suffix) |
| 145 | compiler_args += [ |
| 146 | '--chunk {}:1:{}: {}'.format(chunk_name, common_chunk_name, s) |
| 147 | ] |
| 148 | |
Christopher Lam | c756674 | 2018-07-18 03:02:06 | [diff] [blame] | 149 | if args.checks_only: |
| 150 | compiler_args += ['--checks-only'] |
| 151 | open(args.output, 'w').close() |
| 152 | |
Dan Beam | 0135f0c | 2019-01-23 02:18:37 | [diff] [blame] | 153 | returncode, errors = compiler.Compiler().run_jar(args.compiler, compiler_args) |
Mike Bjorge | 61bf88e | 2017-05-14 07:34:06 | [diff] [blame] | 154 | if returncode != 0: |
Luciano Pacheco | aab13d41 | 2021-01-06 08:58:18 | [diff] [blame] | 155 | print(args.compiler, ' '.join(compiler_args)) |
| 156 | print(errors) |
Mike Bjorge | 61bf88e | 2017-05-14 07:34:06 | [diff] [blame] | 157 | |
| 158 | return returncode |
damargulis | 5c5337d | 2017-04-21 22:36:08 | [diff] [blame] | 159 | |
| 160 | |
| 161 | if __name__ == '__main__': |
Mike Bjorge | 61bf88e | 2017-05-14 07:34:06 | [diff] [blame] | 162 | sys.exit(main()) |