50
50
import os, sys, shutil, tempfile, subprocess, shlex, time, re, logging
51
51
from subprocess import PIPE, STDOUT
52
from tools import shared
52
from tools import shared, jsrun
53
53
from tools.shared import Compression, execute, suffix, unsuffixed, unsuffixed_basename
54
54
from tools.response_file import read_response_file
56
logging = logging.getLogger('emcc')
56
CXX_SUFFIXES = ('.cpp', '.cxx', '.cc')
57
SOURCE_SUFFIXES = ('.c', '.cpp', '.cxx', '.cc', '.m', '.mm')
58
BITCODE_SUFFIXES = ('.bc', '.o', '.obj')
59
DYNAMICLIB_SUFFIXES = ('.dylib', '.so', '.dll')
60
STATICLIB_SUFFIXES = ('.a',)
61
ASSEMBLY_SUFFIXES = ('.ll',)
62
LIB_PREFIXES = ('', 'lib')
63
JS_CONTAINING_SUFFIXES = ('js', 'html')
58
65
# Mapping of emcc opt levels to llvm opt levels. We use llvm opt level 3 in emcc opt
59
66
# levels 2 and 3 (emcc 3 is unsafe opts, so unsuitable for the only level to get
184
186
). Note that the path must be absolute, not
187
-g Use debug info. Note that you need this during
188
the last compilation phase from bitcode to
189
JavaScript, or else we will remove it by
190
default in -O1 and above.
191
In -O0, line numbers wil be shown in the
192
generated code. In -O1 and above, the optimizer
193
removes those comments. This flag does however
194
have the effect of disabling anything that
195
causes name mangling or minification (closure
196
or the registerize pass).
189
-g Use debug info. When compiling to bitcode,
190
this is the same as in clang and gcc, it
191
adds debug info to the object files. When
192
compiling from source to JS or bitcode to JS,
193
it is equivalent to -g3 (keep code as debuggable
194
as possible, except for discarding LLVM
195
debug info, so no C/C++ line numbers; use
196
-g4 to get line number debugging info in JS).
198
-g<level> When compiling from bitcode to JS, we can
199
keep the code debuggable to different
200
degrees. Each of these levels builds on the
203
-g0 Make no effort to keep code debuggable.
204
Will discard LLVM debug info. (default
206
-g1 Preserve (do not minify) whitespace
207
-g2 Preserve function names
208
-g3 Preserve variable names
209
-g4 Preserve LLVM debug info (if -g was
210
used when compiling the C/C++ sources),
211
show line number debug comments, and
212
generate source maps. This is the highest
213
level of debuggability. Note that this
214
may make -O1 and above significantly
215
slower because JS optimization will be
216
limited to 1 core. (default in -O0)
198
218
--typed-arrays <mode> 0: No typed arrays
199
219
1: Parallel typed arrays
200
220
2: Shared (C-like) typed arrays (default)
222
--js-opts 0: Prevent JS optimizer from running
223
1: Use JS optimizer (default)
202
225
--llvm-opts <level> 0: No LLVM optimizations (default in -O0)
203
226
1: -O1 LLVM optimizations (default in -O1)
204
227
2: -O2 LLVM optimizations
205
228
3: -O3 LLVM optimizations (default in -O2+)
207
--llvm-lto <level> 0: No LLVM LTO (default in -O2 and below)
208
1: LLVM LTO (default in -O3)
230
--llvm-lto <level> 0: No LLVM LTO (default)
231
1: LLVM LTO is performed
232
2: We combine all the bitcode and run LLVM opt -O3
233
on that (which optimizes across modules, but is
234
not the same as normal LTO), but do not do normal
236
3: We do both 2 and then 1
209
237
Note: If LLVM optimizations are not run
210
(see --llvm-opts), setting this to 1 has no
238
(see --llvm-opts), setting this has no
213
241
--closure <on> 0: No closure compiler (default in -O2 and below)
853
907
memory_init_file = int(newargs[i+1])
855
909
newargs[i+1] = ''
856
elif newargs[i].startswith(('-I/', '-L/')):
857
if not absolute_warning_shown:
858
logging.warning ('-I or -L of an absolute path encountered. If this is to a local system header/library, it may cause problems (local system files make sense for compiling natively on your system, but not necessarily to JavaScript)') # Of course an absolute path to a non-system-specific library or header is fine, and you can ignore this warning. The danger are system headers that are e.g. x86 specific and nonportable. The emscripten bundled headers are modified to be portable, local system ones are generally not
910
elif newargs[i] == '--proxy-to-worker':
911
proxy_to_worker = True
913
elif newargs[i].startswith(('-I', '-L')):
914
path_name = newargs[i][2:]
915
if not absolute_warning_shown and os.path.isabs(path_name):
916
logging.warning ('-I or -L of an absolute path "' + newargs[i] + '" encountered. If this is to a local system header/library, it may cause problems (local system files make sense for compiling natively on your system, but not necessarily to JavaScript). Pass \'-Wno-warn-absolute-paths\' to emcc to hide this warning.') # Of course an absolute path to a non-system-specific library or header is fine, and you can ignore this warning. The danger are system headers that are e.g. x86 specific and nonportable. The emscripten bundled headers are modified to be portable, local system ones are generally not
859
917
absolute_warning_shown = True
860
918
newargs = [ arg for arg in newargs if arg is not '' ]
863
921
if default_cxx_std:
864
922
newargs = newargs + [default_cxx_std]
924
if js_opts is None: js_opts = True
866
925
if llvm_opts is None: llvm_opts = LLVM_OPT_LEVEL[opt_level]
867
if llvm_lto is None: llvm_lto = opt_level >= 3
868
if opt_level <= 0: keep_llvm_debug = keep_js_debug = True # always keep debug in -O0
869
if opt_level > 0: keep_llvm_debug = False # JS optimizer wipes out llvm debug info from being visible
926
if llvm_lto is None and opt_level >= 3: llvm_lto = 3
927
if opt_level == 0: debug_level = 4
870
928
if closure is None and opt_level == 3: closure = True
930
if llvm_lto is None and bind:
931
logging.debug('running lto for embind') # XXX this is a workaround for a pointer issue
934
# TODO: support source maps with js_transform
935
if js_transform and debug_level >= 4:
936
logging.warning('disabling source maps because a js transform is being done')
872
939
if DEBUG: start_time = time.time() # done after parsing arguments, which might affect debug state
1012
1083
shared.Settings.CORRECT_OVERFLOWS = 1
1013
1084
assert not shared.Settings.PGO, 'cannot run PGO in ASM_JS mode'
1015
if shared.Settings.ASSERTIONS and shared.Settings.ALIASING_FUNCTION_POINTERS:
1016
logging.warning('ALIASING_FUNCTION_POINTERS is on, function pointer comparisons may be invalid across types')
1018
1086
if shared.Settings.CORRECT_SIGNS >= 2 or shared.Settings.CORRECT_OVERFLOWS >= 2 or shared.Settings.CORRECT_ROUNDINGS >= 2:
1019
keep_llvm_debug = True # must keep debug info to do line-by-line operations
1087
debug_level = 4 # must keep debug info to do line-by-line operations
1021
if (keep_llvm_debug or keep_js_debug) and closure:
1089
if debug_level > 1 and closure:
1022
1090
logging.warning('disabling closure because debug info was requested')
1023
1091
closure = False
1025
if minify_whitespace is None:
1026
minify_whitespace = opt_level >= 2 and not keep_js_debug
1028
1093
assert shared.LLVM_TARGET in shared.COMPILER_OPTS
1029
1094
if shared.LLVM_TARGET == 'i386-pc-linux-gnu':
1030
1095
shared.Settings.TARGET_X86 = 1
1038
1103
raise Exception('unknown llvm target: ' + str(shared.LLVM_TARGET))
1105
if shared.Settings.USE_TYPED_ARRAYS != 2 and llvm_opts > 0:
1106
logging.warning('disabling LLVM optimizations, need typed arrays mode 2 for them')
1109
if shared.Settings.MAIN_MODULE:
1110
assert not shared.Settings.SIDE_MODULE
1111
shared.Settings.INCLUDE_FULL_LIBRARY = 1
1112
elif shared.Settings.SIDE_MODULE:
1113
assert not shared.Settings.MAIN_MODULE
1115
if shared.Settings.MAIN_MODULE or shared.Settings.SIDE_MODULE:
1116
assert not memory_init_file, 'memory init file is not supported with module linking'
1117
shared.Settings.LINKABLE = 1 # TODO: add FORCE_DCE option for the brave people that do want to dce here and in side modules
1118
debug_level = max(debug_level, 2)
1120
if shared.Settings.ASSERTIONS and shared.Settings.ALIASING_FUNCTION_POINTERS:
1121
logging.warning('ALIASING_FUNCTION_POINTERS is on, function pointer comparisons may be invalid across types')
1123
if shared.Settings.STB_IMAGE and final_suffix in JS_CONTAINING_SUFFIXES:
1124
input_files.append(shared.path_from_root('third_party', 'stb_image.c'))
1125
shared.Settings.EXPORTED_FUNCTIONS += ['_stbi_load', '_stbi_load_from_memory', '_stbi_image_free']
1127
if type(shared.Settings.EXPORTED_FUNCTIONS) in (list, tuple):
1128
# always need malloc and free to be kept alive and exported, for internal use and other modules
1129
for required_export in ['_malloc', '_free']:
1130
if required_export not in shared.Settings.EXPORTED_FUNCTIONS:
1131
shared.Settings.EXPORTED_FUNCTIONS.append(required_export)
1133
logging.debug('using response file for EXPORTED_FUNCTIONS, make sure it includes _malloc and _free')
1135
if shared.Settings.ASM_JS and shared.Settings.DLOPEN_SUPPORT:
1136
assert shared.Settings.DISABLE_EXCEPTION_CATCHING, 'no exceptions support with dlopen in asm yet'
1139
shared.Settings.PROXY_TO_WORKER = 1
1040
1141
## Compile source code to bitcode
1042
1143
logging.debug('compiling to bitcode')
1076
1179
shared.Building.llvm_as(input_file, temp_file)
1077
1180
temp_files.append(temp_file)
1079
if not LEAVE_INPUTS_RAW: assert len(temp_files) == len(input_files)
1182
if not LEAVE_INPUTS_RAW:
1183
assert len(temp_files) == len(input_files)
1185
# Optimize source files
1187
for i, input_file in enumerate(input_files):
1188
if input_file.endswith(SOURCE_SUFFIXES):
1189
temp_file = temp_files[i]
1190
logging.debug('optimizing %s with -O%d' % (input_file, llvm_opts))
1191
shared.Building.llvm_opt(temp_file, llvm_opts)
1081
1193
# If we were just asked to generate bitcode, stop there
1082
1194
if final_suffix not in JS_CONTAINING_SUFFIXES:
1084
if not os.environ.get('EMCC_OPTIMIZE_NORMALLY'):
1085
logging.warning('-Ox flags ignored, since not generating JavaScript')
1087
for input_file in input_files:
1088
if input_file.endswith(SOURCE_SUFFIXES):
1089
logging.debug('optimizing %s with -O%d since EMCC_OPTIMIZE_NORMALLY defined' % (input_file, llvm_opts))
1090
shared.Building.llvm_opt(in_temp(unsuffixed(uniquename(input_file)) + '.o'), llvm_opts)
1092
logging.debug('not optimizing %s despite EMCC_OPTIMIZE_NORMALLY since not source code' % (input_file))
1093
1195
if not specified_target:
1094
1196
for input_file in input_files:
1095
1197
shutil.move(in_temp(unsuffixed(uniquename(input_file)) + '.o'), unsuffixed_basename(input_file) + '.' + final_suffix)
1136
1244
musl_internal_includes = shared.path_from_root('system', 'lib', 'libc', 'musl', 'src', 'internal')
1137
1245
for src in files:
1138
1246
o = in_temp(os.path.basename(src) + '.o')
1139
execute([shared.PYTHON, shared.EMCC, shared.path_from_root('system', 'lib', src), '-o', o, '-I', musl_internal_includes], stdout=stdout, stderr=stderr)
1247
execute([shared.PYTHON, shared.EMCC, shared.path_from_root('system', 'lib', src), '-o', o, '-I', musl_internal_includes] + lib_opts, stdout=stdout, stderr=stderr)
1141
1249
if prev_cxx: os.environ['EMMAKEN_CXX'] = prev_cxx
1142
1250
shared.Building.link(o_s, in_temp(lib_filename))
1349
1465
for haz in has: # remove symbols that are supplied by another of the inputs
1350
1466
if haz in need:
1351
1467
need.remove(haz)
1352
logging.debug('considering %s: we need %s and have %s' % (name, str(need), str(has)))
1353
if (force or len(need) > 0) and apply_(need):
1354
# We need to build and link the library in
1355
logging.debug('including %s' % name)
1356
libfile = shared.Cache.get(name, create)
1357
extra_files_to_link.append(libfile)
1468
if shared.Settings.VERBOSE: logging.debug('considering %s: we need %s and have %s' % (name, str(need), str(has)))
1469
if force_this or len(need) > 0:
1472
# We need to build and link the library in
1473
logging.debug('including %s' % name)
1474
libfile = shared.Cache.get(name, create)
1475
extra_files_to_link.append(libfile)
1360
1477
# First, combine the bitcode files if there are several. We must also link if we have a singleton .a
1361
1478
if len(input_files) + len(extra_files_to_link) > 1 or \
1394
1511
# Optimize, if asked to
1395
1512
if not LEAVE_INPUTS_RAW:
1396
link_opts = [] if keep_llvm_debug else ['-strip-debug'] # remove LLVM debug info in -O1+, since the optimizer removes it anyhow
1398
if not os.environ.get('EMCC_OPTIMIZE_NORMALLY'):
1399
shared.Building.llvm_opt(in_temp(target_basename + '.bc'), llvm_opts)
1400
if DEBUG: save_intermediate('opt', 'bc')
1401
# Do LTO in a separate pass to work around LLVM bug XXX (see failure e.g. in cubescript)
1403
logging.debug('not running opt because EMCC_OPTIMIZE_NORMALLY was specified, opt should have been run before')
1404
if shared.Building.can_build_standalone():
1405
# If we can LTO, do it before dce, since it opens up dce opportunities
1406
if llvm_lto and shared.Building.can_use_unsafe_opts():
1407
if not shared.Building.can_inline(): link_opts.append('-disable-inlining')
1408
# do not internalize in std-link-opts - it ignores internalize-public-api-list - and add a manual internalize
1409
link_opts += ['-disable-internalize'] + shared.Building.get_safe_internalize() + ['-std-link-opts']
1411
# At minimum remove dead functions etc., this potentially saves a lot in the size of the generated code (and the time to compile it)
1412
link_opts += shared.Building.get_safe_internalize() + ['-globaldce']
1413
shared.Building.llvm_opt(in_temp(target_basename + '.bc'), link_opts)
1414
if DEBUG: save_intermediate('linktime', 'bc')
1513
link_opts = [] if debug_level >= 4 else ['-strip-debug'] # remove LLVM debug if we are not asked for it
1516
logging.debug('running LLVM opt -O3 as pre-LTO')
1517
shared.Building.llvm_opt(in_temp(target_basename + '.bc'), ['-O3'])
1518
if DEBUG: save_intermediate('opt', 'bc')
1520
# If we can LTO, do it before dce, since it opens up dce opportunities
1521
if shared.Building.can_build_standalone() and llvm_lto and llvm_lto != 2 and shared.Building.can_use_unsafe_opts():
1522
if not shared.Building.can_inline(): link_opts.append('-disable-inlining')
1523
# do not internalize in std-link-opts - it ignores internalize-public-api-list - and add a manual internalize
1524
link_opts += ['-disable-internalize'] + shared.Building.get_safe_internalize() + ['-std-link-opts']
1526
# At minimum remove dead functions etc., this potentially saves a lot in the size of the generated code (and the time to compile it)
1527
link_opts += shared.Building.get_safe_internalize() + ['-globaldce']
1528
shared.Building.llvm_opt(in_temp(target_basename + '.bc'), link_opts)
1529
if DEBUG: save_intermediate('linktime', 'bc')
1417
1532
shutil.copyfile(final, save_bc)
1481
1596
final += '.tr.js'
1482
1597
posix = True if not shared.WINDOWS else False
1483
1598
logging.debug('applying transform: %s' % js_transform)
1484
execute(shlex.split(js_transform, posix=posix) + [os.path.abspath(final)])
1599
subprocess.check_call(shlex.split(js_transform, posix=posix) + [os.path.abspath(final)])
1485
1600
if DEBUG: save_intermediate('transformed')
1602
js_transform_tempfiles = [final]
1604
if memory_init_file:
1605
if shared.Settings.USE_TYPED_ARRAYS != 2:
1606
if type(memory_init_file) == int: logging.warning('memory init file requires typed arrays mode 2')
1608
memfile = target + '.mem'
1609
shared.try_delete(memfile)
1611
# handle chunking of the memory initializer
1612
s = re.sub('[\[\]\n\(\)\. ]', '', m.groups(0)[0])
1613
s = s.replace('concat', ',')
1614
if s[-1] == ',': s = s[:-1]
1615
open(memfile, 'wb').write(''.join(map(lambda x: chr(int(x or '0')), s.split(','))))
1617
# Copy into temp dir as well, so can be run there too
1618
temp_memfile = os.path.join(shared.EMSCRIPTEN_TEMP_DIR, os.path.basename(memfile))
1619
if os.path.abspath(memfile) != os.path.abspath(memfile):
1620
shutil.copyfile(memfile, temp_memfile)
1621
return 'loadMemoryInitializer("%s");' % os.path.basename(memfile)
1622
src = re.sub(shared.JS.memory_initializer_pattern, repl, open(final).read(), count=1)
1623
open(final + '.mem.js', 'w').write(src)
1625
js_transform_tempfiles[-1] = final # simple text substitution preserves comment line number mappings
1627
if os.path.exists(memfile):
1628
save_intermediate('meminit')
1629
logging.debug('wrote memory initialization to %s' % memfile)
1631
logging.debug('did not see memory initialization')
1487
1633
# It is useful to run several js optimizer passes together, to save on unneeded unparsing/reparsing
1488
1634
js_optimizer_queue = []
1635
js_optimizer_extra_info = {}
1489
1636
def flush_js_optimizer_queue():
1490
global final, js_optimizer_queue
1637
global final, js_optimizer_queue, js_optimizer_extra_info
1638
if len(js_optimizer_extra_info) == 0:
1639
js_optimizer_extra_info = None
1491
1640
if len(js_optimizer_queue) > 0 and not(len(js_optimizer_queue) == 1 and js_optimizer_queue[0] == 'last'):
1492
1641
if DEBUG != '2':
1493
1642
if shared.Settings.ASM_JS:
1494
1643
js_optimizer_queue = ['asm'] + js_optimizer_queue
1495
1644
logging.debug('applying js optimization passes: %s', js_optimizer_queue)
1496
final = shared.Building.js_optimizer(final, js_optimizer_queue, jcache)
1645
final = shared.Building.js_optimizer(final, js_optimizer_queue, jcache, debug_level >= 4, js_optimizer_extra_info)
1646
js_transform_tempfiles.append(final)
1497
1647
if DEBUG: save_intermediate('js_opts')
1499
1649
for name in js_optimizer_queue:
1501
1651
if shared.Settings.ASM_JS:
1502
1652
passes = ['asm'] + passes
1503
1653
logging.debug('applying js optimization pass: %s', passes)
1504
final = shared.Building.js_optimizer(final, passes, jcache)
1654
final = shared.Building.js_optimizer(final, passes, jcache, debug_level >= 4, js_optimizer_extra_info)
1655
js_transform_tempfiles.append(final)
1505
1656
save_intermediate(name)
1506
1657
js_optimizer_queue = []
1658
js_optimizer_extra_info = {}
1660
if opt_level >= 1 and js_opts:
1509
1661
logging.debug('running pre-closure post-opts')
1511
1663
if DEBUG == '2':
1512
1664
# Clean up the syntax a bit
1513
final = shared.Building.js_optimizer(final, [], jcache)
1665
final = shared.Building.js_optimizer(final, [], jcache, debug_level >= 4)
1666
js_transform_tempfiles.append(final)
1514
1667
if DEBUG: save_intermediate('pretty')
1516
1669
def get_eliminate():
1520
1673
return 'eliminate'
1522
js_optimizer_queue += [get_eliminate(), 'simplifyExpressionsPre']
1524
if shared.Settings.RELOOP and not shared.Settings.ASM_JS:
1525
js_optimizer_queue += ['optimizeShiftsAggressive', get_eliminate()] # aggressive shifts optimization requires loops, it breaks on switches
1675
js_optimizer_queue += [get_eliminate(), 'simplifyExpressions']
1527
1677
if closure and not shared.Settings.ASM_JS:
1528
1678
flush_js_optimizer_queue()
1530
1680
logging.debug('running closure')
1681
# no need to add this to js_transform_tempfiles, because closure and
1682
# debug_level > 0 are never simultaneously true
1531
1683
final = shared.Building.closure_compiler(final)
1532
1684
if DEBUG: save_intermediate('closure')
1535
logging.debug('running post-closure post-opts')
1536
js_optimizer_queue += ['simplifyExpressionsPost']
1538
if (not closure or shared.Settings.ASM_JS) and shared.Settings.RELOOP and not keep_js_debug:
1539
# do this if closure is not enabled (it gives similar speedups), and we do not need to keep debug info around
1540
js_optimizer_queue += ['registerize']
1542
if minify_whitespace:
1543
js_optimizer_queue += ['compress']
1545
if closure and shared.Settings.ASM_JS:
1546
js_optimizer_queue += ['closure']
1548
js_optimizer_queue += ['last']
1550
flush_js_optimizer_queue()
1687
if shared.Settings.OUTLINING_LIMIT > 0 and shared.Settings.ASM_JS:
1688
js_optimizer_queue += ['outline']
1689
js_optimizer_extra_info['sizeToOutline'] = shared.Settings.OUTLINING_LIMIT
1691
if (not closure or shared.Settings.ASM_JS) and shared.Settings.RELOOP and debug_level < 3:
1692
js_optimizer_queue += ['registerize']
1695
if debug_level < 2 and shared.Settings.ASM_JS: js_optimizer_queue = map(lambda p: p if p != 'registerize' else 'registerizeAndMinify', js_optimizer_queue)
1696
if debug_level == 0: js_optimizer_queue += ['minifyWhitespace']
1698
if closure and shared.Settings.ASM_JS:
1699
js_optimizer_queue += ['closure']
1701
if not shared.Settings.SIDE_MODULE: js_optimizer_queue += ['last'] # side modules are not finalized until after relocation
1703
flush_js_optimizer_queue()
1552
1705
# Remove some trivial whitespace # TODO: do not run when compress has already been done on all parts of the code
1553
1706
src = open(final).read()
1554
1707
src = re.sub(r'\n+[ \n]*\n+', '\n', src)
1555
1708
open(final, 'w').write(src)
1557
if memory_init_file:
1558
if shared.Settings.USE_TYPED_ARRAYS != 2:
1559
if type(memory_init_file) == int: logging.warning('memory init file requires typed arrays mode 2')
1561
memfile = target + '.mem'
1562
shared.try_delete(memfile)
1564
# handle chunking of the memory initializer
1565
s = re.sub('[\[\]\n\(\)\. ]', '', m.groups(0)[0])
1566
s = s.replace('concat', ',')
1567
if s[-1] == ',': s = s[:-1]
1568
open(memfile, 'wb').write(''.join(map(lambda x: chr(int(x or '0')), s.split(','))))
1570
# Copy into temp dir as well, so can be run there too
1571
temp_memfile = os.path.join(shared.EMSCRIPTEN_TEMP_DIR, os.path.basename(memfile))
1572
if os.path.abspath(memfile) != os.path.abspath(memfile):
1573
shutil.copyfile(memfile, temp_memfile)
1574
return 'loadMemoryInitializer("%s");' % os.path.basename(memfile)
1575
src = re.sub('/\* memory initializer \*/ allocate\(([\d,\.concat\(\)\[\]\\n ]+)"i8", ALLOC_NONE, Runtime\.GLOBAL_BASE\)', repl, src, count=1)
1576
open(final + '.mem.js', 'w').write(src)
1579
if os.path.exists(memfile):
1580
save_intermediate('meminit')
1581
logging.debug('wrote memory initialization to %s' % memfile)
1583
logging.debug('did not see memory initialization')
1710
def generate_source_map(map_file_base_name, offset=0):
1711
jsrun.run_js(shared.path_from_root('tools', 'source-maps', 'sourcemapper.js'),
1712
shared.NODE_JS, js_transform_tempfiles +
1713
['--sourceRoot', os.getcwd(),
1714
'--mapFileBaseName', map_file_base_name,
1715
'--offset', str(offset)])
1585
1717
# If we were asked to also generate HTML, do that
1586
1718
if final_suffix == 'html':
1587
1719
logging.debug('generating HTML')
1588
1720
shell = open(shell_path).read()
1589
1721
html = open(target, 'w')
1590
if not Compression.on:
1723
html.write(shell.replace('{{{ SCRIPT_CODE }}}', open(shared.path_from_root('src', 'proxyClient.js')).read().replace('{{{ filename }}}', target_basename)))
1724
js_target = unsuffixed(target) + '.js'
1725
shutil.copyfile(final, js_target)
1726
elif not Compression.on:
1727
if debug_level >= 4:
1728
match = re.match('.*?<script[^>]*>{{{ SCRIPT_CODE }}}</script>', shell,
1731
raise RuntimeError('''Could not find script insertion point - make sure you have <script type='text/javascript'>{{{ SCRIPT_CODE }}}</script> in your HTML file (with no newlines)''')
1732
generate_source_map(target, match.group().count('\n'))
1591
1733
html.write(shell.replace('{{{ SCRIPT_CODE }}}', open(final).read()))
1593
1735
# Compress the main code