OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 import buildbot_common | 6 import buildbot_common |
7 import make_rules | 7 import make_rules |
8 import optparse | 8 import optparse |
9 import os | 9 import os |
10 import sys | 10 import sys |
11 | 11 |
12 from make_rules import BuildDefineList, BuildLibList, BuildToolDict | 12 from make_rules import BuildDefineList, BuildLibList, BuildToolDict |
13 from make_rules import GetBuildRule, BUILD_RULES | 13 from make_rules import GetBuildRule, BUILD_RULES |
14 | 14 |
15 SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) | 15 SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) |
16 SDK_SRC_DIR = os.path.dirname(SCRIPT_DIR) | 16 SDK_SRC_DIR = os.path.dirname(SCRIPT_DIR) |
17 SDK_EXAMPLE_DIR = os.path.join(SDK_SRC_DIR, 'examples') | 17 SDK_EXAMPLE_DIR = os.path.join(SDK_SRC_DIR, 'examples') |
18 SDK_DIR = os.path.dirname(SDK_SRC_DIR) | 18 SDK_DIR = os.path.dirname(SDK_SRC_DIR) |
19 SRC_DIR = os.path.dirname(SDK_DIR) | 19 SRC_DIR = os.path.dirname(SDK_DIR) |
20 OUT_DIR = os.path.join(SRC_DIR, 'out') | 20 OUT_DIR = os.path.join(SRC_DIR, 'out') |
21 PPAPI_DIR = os.path.join(SRC_DIR, 'ppapi') | 21 PPAPI_DIR = os.path.join(SRC_DIR, 'ppapi') |
22 | 22 |
23 # Add SDK make tools scripts to the python path. | 23 # Add SDK make tools scripts to the python path. |
24 sys.path.append(os.path.join(SDK_SRC_DIR, 'tools')) | 24 sys.path.append(os.path.join(SDK_SRC_DIR, 'tools')) |
25 import getos | 25 import getos |
26 | 26 |
27 SUPPORTED_HOSTS = ['win'] | 27 SUPPORTED_HOSTS = ['win'] |
28 | 28 |
29 def ErrorExit(text): | 29 def ErrorExit(text): |
30 sys.stderr.write(text + '\n') | 30 ErrorMsgFunc(text) |
31 sys.exit(1) | 31 sys.exit(1) |
32 | 32 |
33 | 33 |
34 def Replace(text, replacements): | 34 def Replace(text, replacements): |
35 for key in replacements: | 35 for key in replacements: |
36 val = replacements[key] | 36 val = replacements[key] |
37 if val is not None: | 37 if val is not None: |
38 text = text.replace(key, val) | 38 text = text.replace(key, val) |
39 return text | 39 return text |
40 | 40 |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
80 | 80 |
81 def GetSourcesDict(sources): | 81 def GetSourcesDict(sources): |
82 source_map = {} | 82 source_map = {} |
83 for key in ['.c', '.cc']: | 83 for key in ['.c', '.cc']: |
84 source_list = [fname for fname in sources if fname.endswith(key)] | 84 source_list = [fname for fname in sources if fname.endswith(key)] |
85 if source_list: | 85 if source_list: |
86 source_map[key] = source_list | 86 source_map[key] = source_list |
87 else: | 87 else: |
88 source_map[key] = [] | 88 source_map[key] = [] |
89 return source_map | 89 return source_map |
90 | 90 |
91 | 91 |
92 def GetPlatforms(plat_list, plat_filter): | 92 def GetPlatforms(plat_list, plat_filter): |
93 platforms = [] | 93 platforms = [] |
94 for plat in plat_list: | 94 for plat in plat_list: |
95 if plat in plat_filter: | 95 if plat in plat_filter: |
96 platforms.append(plat) | 96 platforms.append(plat) |
97 return platforms | 97 return platforms |
98 | 98 |
99 | 99 |
100 def GenerateToolDefaults(desc, tools): | 100 def GenerateToolDefaults(desc, tools): |
101 defaults = '' | 101 defaults = '' |
102 for tool in tools: | 102 for tool in tools: |
103 defaults += BUILD_RULES[tool]['DEFS'] | 103 defaults += BUILD_RULES[tool]['DEFS'] |
104 return defaults | 104 return defaults |
105 | 105 |
106 | 106 |
107 def GenerateSettings(desc, tools): | 107 def GenerateSettings(desc, tools): |
108 settings = SetVar('VALID_TOOLCHAINS', tools) | 108 settings = SetVar('VALID_TOOLCHAINS', tools) |
109 settings+= 'TOOLCHAIN?=%s\n\n' % tools[0] | 109 settings+= 'TOOLCHAIN?=%s\n\n' % tools[0] |
110 for target in desc['TARGETS']: | 110 for target in desc['TARGETS']: |
111 name = target['NAME'] | 111 name = target['NAME'] |
112 macro = name.upper() | 112 macro = name.upper() |
113 srcs = GetSourcesDict(target['SOURCES']) | 113 srcs = GetSourcesDict(target['SOURCES']) |
114 | 114 |
(...skipping 15 matching lines...) Expand all Loading... |
130 def GetTarget(tool, targ_type, replace): | 130 def GetTarget(tool, targ_type, replace): |
131 pattern = BUILD_RULES[tool]['TOOL'][targ_type] | 131 pattern = BUILD_RULES[tool]['TOOL'][targ_type] |
132 return Replace(pattern, replace) | 132 return Replace(pattern, replace) |
133 | 133 |
134 | 134 |
135 def GenerateCompile(target, tool, arch, srcs): | 135 def GenerateCompile(target, tool, arch, srcs): |
136 """Generates a Compile target. | 136 """Generates a Compile target. |
137 | 137 |
138 For the given target, toolset and architecture, returns a rule to generate | 138 For the given target, toolset and architecture, returns a rule to generate |
139 the object files for the set of sources. | 139 the object files for the set of sources. |
140 | 140 |
141 Returns: | 141 Returns: |
142 Returns a tuple containin the objects and the rule. | 142 Returns a tuple containin the objects and the rule. |
143 """ | 143 """ |
144 rules = '' | 144 rules = '' |
145 name = target['NAME'] | 145 name = target['NAME'] |
146 object_sets = [] | 146 object_sets = [] |
147 | 147 |
148 defines = target.get('DEFINES', []) | 148 defines = target.get('DEFINES', []) |
149 defs = BuildDefineList(tool, defines) | 149 defs = BuildDefineList(tool, defines) |
150 | 150 |
151 if srcs['.c']: | 151 if srcs['.c']: |
152 replace = BuildToolDict(tool, name, arch, 'c', DEFLIST=defs) | 152 replace = BuildToolDict(tool, name, arch, 'c', DEFLIST=defs) |
153 compile_rule = GetBuildRule(tool, 'CC') | 153 compile_rule = GetBuildRule(tool, 'CC') |
154 rules += Replace(compile_rule, replace) | 154 rules += Replace(compile_rule, replace) |
155 object_sets.append('$(%s)' % replace['<OBJS>']) | 155 object_sets.append('$(%s)' % replace['<OBJS>']) |
156 | 156 |
157 if srcs['.cc']: | 157 if srcs['.cc']: |
158 replace = BuildToolDict(tool, name, arch, 'cc', DEFLIST=defs) | 158 replace = BuildToolDict(tool, name, arch, 'cc', DEFLIST=defs) |
159 compile_rule = GetBuildRule(tool, 'CXX') | 159 compile_rule = GetBuildRule(tool, 'CXX') |
160 rules += Replace(compile_rule, replace) | 160 rules += Replace(compile_rule, replace) |
161 object_sets.append('$(%s)' % replace['<OBJS>']) | 161 object_sets.append('$(%s)' % replace['<OBJS>']) |
| 162 |
162 return (' '.join(object_sets), rules) | 163 return (' '.join(object_sets), rules) |
163 | 164 |
164 | 165 |
165 def GenerateLink(target, tool, arch, objs): | 166 def GenerateLink(target, tool, arch, objs): |
166 """Generate a Link target. | 167 """Generate a Link target. |
167 | 168 |
168 Returns: | 169 Returns: |
169 Returns a tuple containing the rule and target. | 170 Returns a tuple containing the rule and target. |
170 """ | 171 """ |
171 targ_type = target['TYPE'] | 172 targ_type = target['TYPE'] |
172 link_rule = GetBuildRule(tool, targ_type.upper()) | 173 link_rule = GetBuildRule(tool, targ_type.upper()) |
173 libs = target.get('LIBS', []) | 174 libs = target.get('LIBS', []) |
174 libs = BuildLibList(tool, libs) | 175 libs = BuildLibList(tool, libs) |
175 replace = BuildToolDict(tool, target['NAME'], arch, 'nexe', | 176 replace = BuildToolDict(tool, target['NAME'], arch, 'nexe', |
176 OBJS=objs, LIBLIST=libs) | 177 OBJS=objs, LIBLIST=libs) |
177 rule = Replace(link_rule, replace) | 178 rule = Replace(link_rule, replace) |
178 target_out = GetTarget(tool, targ_type, replace) | 179 target_out = GetTarget(tool, targ_type, replace) |
179 return target_out, rule | 180 return target_out, rule |
180 | 181 |
181 | 182 |
182 def GenerateNMF(target, tool): | 183 def GenerateNMF(target, tool): |
183 nmf_rule = BUILD_RULES[tool]['NMF'] | 184 nmf_rule = BUILD_RULES[tool]['NMF'] |
184 replace = BuildToolDict(tool, target['NAME']) | 185 replace = BuildToolDict(tool, target['NAME']) |
185 rule = Replace(nmf_rule, replace) | 186 rule = Replace(nmf_rule, replace) |
186 target_out = GetTarget(tool, 'nmf', replace) | 187 target_out = GetTarget(tool, 'nmf', replace) |
187 return target_out, rule | 188 return target_out, rule |
(...skipping 18 matching lines...) Expand all Loading... |
206 | 207 |
207 if target['TYPE'] == 'main': | 208 if target['TYPE'] == 'main': |
208 main = target | 209 main = target |
209 elif target['TYPE'] == 'lib': | 210 elif target['TYPE'] == 'lib': |
210 all_targets.append(targs) | 211 all_targets.append(targs) |
211 | 212 |
212 if main: | 213 if main: |
213 targs, nmf_rule = GenerateNMF(main, tc) | 214 targs, nmf_rule = GenerateNMF(main, tc) |
214 rules += nmf_rule | 215 rules += nmf_rule |
215 all_targets.append(targs) | 216 all_targets.append(targs) |
216 rules += '\n.PHONY : clean\nclean:\n\t$(RM) ' + ' '.join(clean) | 217 rules += '\n.PHONY : clean\nclean:\n\t$(RM) $(DEPFILES) ' + ' '.join(clean) |
| 218 rules += '\n\n-include $(DEPFILES)' |
217 return ' '.join(all_targets), rules | 219 return ' '.join(all_targets), rules |
218 | 220 |
219 | 221 |
220 | |
221 def GenerateTargets(desc, tools): | 222 def GenerateTargets(desc, tools): |
222 targets = [] | 223 targets = [] |
223 rules = '' | 224 rules = '' |
224 for tc in tools: | 225 for tc in tools: |
225 for target in desc['TARGETS']: | 226 for target in desc['TARGETS']: |
226 name = target['NAME'] | 227 name = target['NAME'] |
227 replace = BuildToolDict(tc, name) | 228 replace = BuildToolDict(tc, name) |
228 target = GetTarget(tc, target['TYPE'], replace) | 229 target = GetTarget(tc, target['TYPE'], replace) |
229 if target: | 230 if target: |
230 targets.append(target) | 231 targets.append(target) |
(...skipping 19 matching lines...) Expand all Loading... |
250 '__PROJECT_SETTINGS__' : settings, | 251 '__PROJECT_SETTINGS__' : settings, |
251 '__PROJECT_TARGETS__' : target_def, | 252 '__PROJECT_TARGETS__' : target_def, |
252 '__PROJECT_TOOLS__' : tool_def, | 253 '__PROJECT_TOOLS__' : tool_def, |
253 '__PROJECT_RULES__' : rules, | 254 '__PROJECT_RULES__' : rules, |
254 '__PROJECT_PRELAUNCH__' : prelaunch, | 255 '__PROJECT_PRELAUNCH__' : prelaunch, |
255 '__PROJECT_PRERUN__' : prerun, | 256 '__PROJECT_PRERUN__' : prerun, |
256 '__PROJECT_POSTLAUNCH__' : postlaunch | 257 '__PROJECT_POSTLAUNCH__' : postlaunch |
257 } | 258 } |
258 | 259 |
259 | 260 |
260 | |
261 # 'KEY' : ( <TYPE>, [Accepted Values], <Required?>) | 261 # 'KEY' : ( <TYPE>, [Accepted Values], <Required?>) |
262 DSC_FORMAT = { | 262 DSC_FORMAT = { |
263 'TOOLS' : (list, ['newlib', 'glibc', 'pnacl', 'win'], True), | 263 'TOOLS' : (list, ['newlib', 'glibc', 'pnacl', 'win'], True), |
264 'PREREQ' : (list, '', False), | 264 'PREREQ' : (list, '', False), |
265 'TARGETS' : (list, { | 265 'TARGETS' : (list, { |
266 'NAME': (str, '', True), | 266 'NAME': (str, '', True), |
267 'TYPE': (str, ['main', 'nexe', 'lib', 'so'], True), | 267 'TYPE': (str, ['main', 'nexe', 'lib', 'so'], True), |
268 'SOURCES': (list, '', True), | 268 'SOURCES': (list, '', True), |
269 'CCFLAGS': (list, '', False), | 269 'CCFLAGS': (list, '', False), |
270 'CXXFLAGS': (list, '', False), | 270 'CXXFLAGS': (list, '', False), |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
303 | 303 |
304 # For each provided key, verify it's valid | 304 # For each provided key, verify it's valid |
305 for key in src: | 305 for key in src: |
306 # Verify the key is known | 306 # Verify the key is known |
307 if key not in format: | 307 if key not in format: |
308 ErrorMsg('Unexpected key %s.' % key) | 308 ErrorMsg('Unexpected key %s.' % key) |
309 failed = True | 309 failed = True |
310 continue | 310 continue |
311 | 311 |
312 exp_type, exp_value, required = format[key] | 312 exp_type, exp_value, required = format[key] |
313 value = src[key] | 313 value = src[key] |
314 | 314 |
315 # Verify the key is of the expected type | 315 # Verify the key is of the expected type |
316 if exp_type != type(value): | 316 if exp_type != type(value): |
317 ErrorMsg('Key %s expects %s not %s.' % ( | 317 ErrorMsg('Key %s expects %s not %s.' % ( |
318 key, exp_type.__name__.upper(), type(value).__name__.upper())) | 318 key, exp_type.__name__.upper(), type(value).__name__.upper())) |
319 failed = True | 319 failed = True |
320 continue | 320 continue |
321 | 321 |
322 # Verify the value is non-empty if required | 322 # Verify the value is non-empty if required |
323 if required and not value: | 323 if required and not value: |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
402 def IsNexe(desc): | 402 def IsNexe(desc): |
403 for target in desc['TARGETS']: | 403 for target in desc['TARGETS']: |
404 if target['TYPE'] == 'main': | 404 if target['TYPE'] == 'main': |
405 return True | 405 return True |
406 return False | 406 return False |
407 | 407 |
408 | 408 |
409 def ProcessHTML(srcroot, dstroot, desc, toolchains): | 409 def ProcessHTML(srcroot, dstroot, desc, toolchains): |
410 name = desc['NAME'] | 410 name = desc['NAME'] |
411 outdir = os.path.join(dstroot, desc['DEST'], name) | 411 outdir = os.path.join(dstroot, desc['DEST'], name) |
412 | 412 |
413 srcfile = os.path.join(srcroot, 'index.html') | 413 srcfile = os.path.join(srcroot, 'index.html') |
414 tools = GetPlatforms(toolchains, desc['TOOLS']) | 414 tools = GetPlatforms(toolchains, desc['TOOLS']) |
415 for tool in tools: | 415 for tool in tools: |
416 dstfile = os.path.join(outdir, 'index_%s.html' % tool); | 416 dstfile = os.path.join(outdir, 'index_%s.html' % tool); |
417 print 'Writting from %s to %s' % (srcfile, dstfile) | 417 print 'Writting from %s to %s' % (srcfile, dstfile) |
418 replace = { | 418 replace = { |
419 '<NAME>': name, | 419 '<NAME>': name, |
420 '<TITLE>': desc['TITLE'], | 420 '<TITLE>': desc['TITLE'], |
421 '<tc>': tool | 421 '<tc>': tool |
422 } | 422 } |
423 WriteReplaced(srcfile, dstfile, replace) | 423 WriteReplaced(srcfile, dstfile, replace) |
424 | 424 |
425 replace['<tc>'] = tools[0] | 425 replace['<tc>'] = tools[0] |
426 srcfile = os.path.join(SDK_SRC_DIR, 'build_tools', 'redirect.html') | 426 srcfile = os.path.join(SDK_SRC_DIR, 'build_tools', 'redirect.html') |
427 dstfile = os.path.join(outdir, 'index.html') | 427 dstfile = os.path.join(outdir, 'index.html') |
428 WriteReplaced(srcfile, dstfile, replace) | 428 WriteReplaced(srcfile, dstfile, replace) |
429 | 429 |
430 | 430 |
431 def LoadProject(filename, toolchains): | 431 def LoadProject(filename, toolchains): |
432 """Generate a Master Makefile that builds all examples. | 432 """Generate a Master Makefile that builds all examples. |
433 | 433 |
434 Load a project desciption file, verifying it conforms and checking | 434 Load a project desciption file, verifying it conforms and checking |
435 if it matches the set of requested toolchains. Return None if the | 435 if it matches the set of requested toolchains. Return None if the |
436 project is filtered out.""" | 436 project is filtered out.""" |
437 | 437 |
438 print '\n\nProcessing %s...' % filename | 438 print '\n\nProcessing %s...' % filename |
439 # Default src directory is the directory the description was found in | 439 # Default src directory is the directory the description was found in |
440 desc = open(filename, 'r').read() | 440 desc = open(filename, 'r').read() |
441 desc = eval(desc, {}, {}) | 441 desc = eval(desc, {}, {}) |
442 | 442 |
443 # Verify the format of this file | 443 # Verify the format of this file |
444 if not ValidateFormat(desc, DSC_FORMAT): | 444 if not ValidateFormat(desc, DSC_FORMAT): |
445 ErrorExit('Failed to validate: ' + filename) | 445 ErrorExit('Failed to validate: ' + filename) |
446 | 446 |
447 # Check if we are actually interested in this example | 447 # Check if we are actually interested in this example |
448 match = False | 448 match = False |
449 for toolchain in toolchains: | 449 for toolchain in toolchains: |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
504 | 504 |
505 | 505 |
506 def GenerateMasterMakefile(in_path, out_path, projects): | 506 def GenerateMasterMakefile(in_path, out_path, projects): |
507 """Generate a Master Makefile that builds all examples. """ | 507 """Generate a Master Makefile that builds all examples. """ |
508 replace = { '__PROJECT_LIST__' : SetVar('PROJECTS', projects) } | 508 replace = { '__PROJECT_LIST__' : SetVar('PROJECTS', projects) } |
509 WriteReplaced(in_path, out_path, replace) | 509 WriteReplaced(in_path, out_path, replace) |
510 | 510 |
511 outdir = os.path.dirname(os.path.abspath(out_path)) | 511 outdir = os.path.dirname(os.path.abspath(out_path)) |
512 pepperdir = os.path.dirname(outdir) | 512 pepperdir = os.path.dirname(outdir) |
513 AddMakeBat(pepperdir, outdir) | 513 AddMakeBat(pepperdir, outdir) |
514 | 514 |
515 | 515 |
516 def main(argv): | 516 def main(argv): |
517 parser = optparse.OptionParser() | 517 parser = optparse.OptionParser() |
518 parser.add_option('--dstroot', help='Set root for destination.', | 518 parser.add_option('--dstroot', help='Set root for destination.', |
519 dest='dstroot', default=os.path.join(OUT_DIR, 'pepper_canary')) | 519 dest='dstroot', default=os.path.join(OUT_DIR, 'pepper_canary')) |
520 parser.add_option('--master', help='Create master Makefile.', | 520 parser.add_option('--master', help='Create master Makefile.', |
521 action='store_true', dest='master', default=False) | 521 action='store_true', dest='master', default=False) |
522 parser.add_option('--newlib', help='Create newlib examples.', | 522 parser.add_option('--newlib', help='Create newlib examples.', |
523 action='store_true', dest='newlib', default=False) | 523 action='store_true', dest='newlib', default=False) |
524 parser.add_option('--glibc', help='Create glibc examples.', | 524 parser.add_option('--glibc', help='Create glibc examples.', |
525 action='store_true', dest='glibc', default=False) | 525 action='store_true', dest='glibc', default=False) |
526 parser.add_option('--pnacl', help='Create pnacl examples.', | 526 parser.add_option('--pnacl', help='Create pnacl examples.', |
527 action='store_true', dest='pnacl', default=False) | 527 action='store_true', dest='pnacl', default=False) |
528 parser.add_option('--host', help='Create host examples.', | 528 parser.add_option('--host', help='Create host examples.', |
529 action='store_true', dest='host', default=False) | 529 action='store_true', dest='host', default=False) |
530 | 530 |
531 toolchains = [] | 531 toolchains = [] |
532 platform = getos.GetPlatform() | 532 platform = getos.GetPlatform() |
533 | 533 |
534 options, args = parser.parse_args(argv) | 534 options, args = parser.parse_args(argv) |
535 if options.newlib: | 535 if options.newlib: |
536 toolchains.append('newlib') | 536 toolchains.append('newlib') |
537 if options.glibc: | 537 if options.glibc: |
538 toolchains.append('glibc') | 538 toolchains.append('glibc') |
539 if options.pnacl: | 539 if options.pnacl: |
540 toolchains.append('pnacl') | 540 toolchains.append('pnacl') |
541 if options.host: | 541 if options.host: |
542 toolchains.append(platform) | 542 toolchains.append(platform) |
543 | 543 |
| 544 if not args: |
| 545 ErrorExit('Please specify one or more projects to generate Makefiles for.') |
| 546 |
544 # By default support newlib and glibc | 547 # By default support newlib and glibc |
545 if not toolchains: | 548 if not toolchains: |
546 toolchains = ['newlib', 'glibc'] | 549 toolchains = ['newlib', 'glibc'] |
547 print 'Using default toolchains: ' + ' '.join(toolchains) | 550 print 'Using default toolchains: ' + ' '.join(toolchains) |
548 | 551 |
549 examples = [] | 552 examples = [] |
550 libs = [] | 553 libs = [] |
551 for filename in args: | 554 for filename in args: |
552 desc = LoadProject(filename, toolchains) | 555 desc = LoadProject(filename, toolchains) |
553 if not desc: | 556 if not desc: |
(...skipping 17 matching lines...) Expand all Loading... |
571 master_in = os.path.join(SDK_EXAMPLE_DIR, 'Makefile') | 574 master_in = os.path.join(SDK_EXAMPLE_DIR, 'Makefile') |
572 master_out = os.path.join(options.dstroot, 'examples', 'Makefile') | 575 master_out = os.path.join(options.dstroot, 'examples', 'Makefile') |
573 GenerateMasterMakefile(master_in, master_out, examples) | 576 GenerateMasterMakefile(master_in, master_out, examples) |
574 master_out = os.path.join(options.dstroot, 'src', 'Makefile') | 577 master_out = os.path.join(options.dstroot, 'src', 'Makefile') |
575 GenerateMasterMakefile(master_in, master_out, libs) | 578 GenerateMasterMakefile(master_in, master_out, libs) |
576 return 0 | 579 return 0 |
577 | 580 |
578 | 581 |
579 if __name__ == '__main__': | 582 if __name__ == '__main__': |
580 sys.exit(main(sys.argv[1:])) | 583 sys.exit(main(sys.argv[1:])) |
OLD | NEW |