~ubuntu-branches/ubuntu/trusty/emscripten/trusty-proposed

« back to all changes in this revision

Viewing changes to tools/file_packager.py

  • Committer: Package Import Robot
  • Author(s): Sylvestre Ledru
  • Date: 2014-01-19 14:12:40 UTC
  • mfrom: (1.2.4)
  • Revision ID: package-import@ubuntu.com-20140119141240-jg1l42cc158j59tn
Tags: 1.9.0~20140119~7dc8c2f-1
* New snapshot release (Closes: #733714)
* Provide sources for javascript and flash. Done in orig-tar.sh
  Available in third_party/websockify/include/web-socket-js/src/
  (Closes: #735903)

Show diffs side-by-side

added added

removed removed

Lines of Context:
11
11
 
12
12
Usage:
13
13
 
14
 
  file_packager.py TARGET [--preload A [B..]] [--embed C [D..]] [--compress COMPRESSION_DATA] [--pre-run] [--crunch[=X]] [--js-output=OUTPUT.js] [--no-force]
15
 
 
16
 
  --pre-run Will generate wrapper code that does preloading in Module.preRun. This is necessary if you add this
17
 
            code before the main file has been loading, which includes necessary components like addRunDependency.
 
14
  file_packager.py TARGET [--preload A [B..]] [--embed C [D..]] [--exclude E [F..]] [--compress COMPRESSION_DATA] [--crunch[=X]] [--js-output=OUTPUT.js] [--no-force] [--use-preload-cache] [--no-heap-copy]
18
15
 
19
16
  --crunch=X Will compress dxt files to crn with quality level X. The crunch commandline tool must be present
20
17
             and CRUNCH should be defined in ~/.emscripten that points to it. JS crunch decompressing code will
30
27
 
31
28
  --use-preload-cache Stores package in IndexedDB so that subsequent loads don't need to do XHR. Checks package version.
32
29
 
 
30
  --no-heap-copy If specified, the preloaded filesystem is not copied inside the Emscripten HEAP, but kept in a separate typed array outside it.
 
31
                 The default, if this is not specified, is to embed the VFS inside the HEAP, so that mmap()ing files in it is a no-op.
 
32
                 Passing this flag optimizes for fread() usage, omitting it optimizes for mmap() usage.
 
33
 
33
34
Notes:
34
35
 
35
36
  * The file packager generates unix-style file paths. So if you are on windows and a file is accessed at
44
45
import shared
45
46
from shared import Compression, execute, suffix, unsuffixed
46
47
from subprocess import Popen, PIPE, STDOUT
 
48
import fnmatch
47
49
 
48
50
if len(sys.argv) == 1:
49
 
  print '''Usage: file_packager.py TARGET [--preload A...] [--embed B...] [--compress COMPRESSION_DATA] [--pre-run] [--crunch[=X]] [--js-output=OUTPUT.js] [--no-force] [--use-preload-cache]
 
51
  print '''Usage: file_packager.py TARGET [--preload A...] [--embed B...] [--exclude C...] [--compress COMPRESSION_DATA] [--crunch[=X]] [--js-output=OUTPUT.js] [--no-force] [--use-preload-cache] [--no-heap-copy]
50
52
See the source for more details.'''
51
53
  sys.exit(0)
52
54
 
65
67
AV_WORKAROUND = 0 # Set to 1 to randomize file order and add some padding, to work around silly av false positives
66
68
 
67
69
data_files = []
68
 
in_preload = False
69
 
in_embed = False
 
70
excluded_patterns = []
 
71
leading = ''
70
72
has_preloaded = False
71
 
in_compress = 0
72
 
pre_run = False
 
73
compress_cnt = 0
73
74
crunch = 0
74
75
plugins = []
75
76
jsoutput = None
76
77
force = True
 
78
# If set to True, IndexedDB (IDBFS in library_idbfs.js) is used to locally cache VFS XHR so that subsequent 
 
79
# page loads can read the data from the offline cache instead.
77
80
use_preload_cache = False
 
81
# If set to True, the blob received from XHR is moved to the Emscripten HEAP, optimizing for mmap() performance.
 
82
# If set to False, the XHR blob is kept intact, and fread()s etc. are performed directly to that data. This optimizes for minimal memory usage and fread() performance.
 
83
no_heap_copy = True
78
84
 
79
 
for arg in sys.argv[1:]:
 
85
for arg in sys.argv[2:]:
80
86
  if arg == '--preload':
81
 
    in_preload = True
82
 
    in_embed = False
83
87
    has_preloaded = True
84
 
    in_compress = 0
 
88
    leading = 'preload'
85
89
  elif arg == '--embed':
86
 
    in_embed = True
87
 
    in_preload = False
88
 
    in_compress = 0
 
90
    leading = 'embed'
 
91
  elif arg == '--exclude':
 
92
    leading = 'exclude'
89
93
  elif arg == '--compress':
 
94
    compress_cnt = 1
90
95
    Compression.on = True
91
 
    in_compress = 1
92
 
    in_preload = False
93
 
    in_embed = False
94
 
  elif arg == '--pre-run':
95
 
    pre_run = True
96
 
    in_preload = False
97
 
    in_embed = False
98
 
    in_compress = 0
 
96
    leading = 'compress'
99
97
  elif arg == '--no-force':
100
98
    force = False
 
99
    leading = ''
101
100
  elif arg == '--use-preload-cache':
102
101
    use_preload_cache = True
 
102
    leading = ''
 
103
  elif arg == '--no-heap-copy':
 
104
    no_heap_copy = False
 
105
    leading = ''
103
106
  elif arg.startswith('--js-output'):
104
107
    jsoutput = arg.split('=')[1] if '=' in arg else None
 
108
    leading = ''
105
109
  elif arg.startswith('--crunch'):
106
110
    from shared import CRUNCH
107
111
    crunch = arg.split('=')[1] if '=' in arg else '128'
108
 
    in_preload = False
109
 
    in_embed = False
110
 
    in_compress = 0
 
112
    leading = ''
111
113
  elif arg.startswith('--plugin'):
112
114
    plugin = open(arg.split('=')[1], 'r').read()
113
115
    eval(plugin) # should append itself to plugins
114
 
    in_preload = False
115
 
    in_embed = False
116
 
    in_compress = 0
117
 
  elif in_preload or in_embed:
118
 
    mode = 'preload'
119
 
    if in_embed:
120
 
      mode = 'embed'
 
116
    leading = ''
 
117
  elif leading == 'preload' or leading == 'embed':
 
118
    mode = leading
121
119
    if '@' in arg:
122
120
      srcpath, dstpath = arg.split('@') # User is specifying destination filename explicitly.
123
121
    else:
126
124
      data_files.append({ 'srcpath': srcpath, 'dstpath': dstpath, 'mode': mode })
127
125
    else:
128
126
      print >> sys.stderr, 'Warning: ' + arg + ' does not exist, ignoring.'
129
 
  elif in_compress:
130
 
    if in_compress == 1:
 
127
  elif leading == 'exclude':
 
128
    excluded_patterns.append(arg)
 
129
  elif leading == 'compress':
 
130
    if compress_cnt == 1:
131
131
      Compression.encoder = arg
132
 
      in_compress = 2
133
 
    elif in_compress == 2:
 
132
      compress_cnt = 2
 
133
    elif compress_cnt == 2:
134
134
      Compression.decoder = arg
135
 
      in_compress = 3
136
 
    elif in_compress == 3:
 
135
      compress_cnt = 3
 
136
    elif compress_cnt == 3:
137
137
      Compression.js_name = arg
138
 
      in_compress = 0
 
138
      compress_cnt = 0
 
139
  else:
 
140
    print >> sys.stderr, 'Unknown parameter:', arg
 
141
    sys.exit(1)
139
142
 
140
143
if (not force) and len(data_files) == 0:
141
144
  has_preloaded = False
142
145
 
143
146
ret = '''
 
147
var Module;
 
148
if (typeof Module === 'undefined') Module = eval('(function() { try { return Module || {} } catch(e) { return {} } })()');
 
149
if (!Module.expectedDataFileDownloads) {
 
150
  Module.expectedDataFileDownloads = 0;
 
151
  Module.finishedDataFileDownloads = 0;
 
152
}
 
153
Module.expectedDataFileDownloads++;
144
154
(function() {
145
155
'''
146
156
 
163
173
    result = False
164
174
  return result
165
175
 
166
 
# The packager should never preload/embed any directories that have a component starting with '.' in them,
167
 
# or if the file is hidden (Win32). Note that this filter ONLY applies to directories. Explicitly specified single files
168
 
# are always preloaded/embedded, even if they start with a '.'.
169
 
def should_ignore(filename):
170
 
  if has_hidden_attribute(filename):
 
176
# The packager should never preload/embed files if the file is hidden (Win32).
 
177
# or it matches any pattern specified in --exclude
 
178
def should_ignore(fullname):
 
179
  if has_hidden_attribute(fullname):
171
180
    return True
172
181
    
173
 
  components = filename.replace('\\\\', '/').replace('\\', '/').split('/')
174
 
  for c in components:
175
 
    if c.startswith('.') and c != '.' and c != '..':
 
182
  for p in excluded_patterns:
 
183
    if fnmatch.fnmatch(fullname, p):
176
184
      return True
177
185
  return False
178
186
 
181
189
  # rootpathsrc: The path name of the root directory on the local FS we are adding to emscripten virtual FS.
182
190
  # rootpathdst: The name we want to make the source path available on the emscripten virtual FS.
183
191
  mode, rootpathsrc, rootpathdst = arg
 
192
  new_names = []
184
193
  for name in names:
185
194
    fullname = os.path.join(dirname, name)
186
 
    if not os.path.isdir(fullname):
187
 
      if should_ignore(fullname):
188
 
        if DEBUG:
189
 
          print >> sys.stderr, 'Skipping hidden file "' + fullname + '" from inclusion in the emscripten virtual file system.'
190
 
      else:
 
195
    if should_ignore(fullname):
 
196
      if DEBUG:
 
197
        print >> sys.stderr, 'Skipping file "' + fullname + '" from inclusion in the emscripten virtual file system.'
 
198
    else:
 
199
      new_names.append(name)
 
200
      if not os.path.isdir(fullname):
191
201
        dstpath = os.path.join(rootpathdst, os.path.relpath(fullname, rootpathsrc)) # Convert source filename relative to root directory of target FS.
192
 
        data_files.append({ 'srcpath': fullname, 'dstpath': dstpath, 'mode': mode })
 
202
        new_data_files.append({ 'srcpath': fullname, 'dstpath': dstpath, 'mode': mode })
 
203
  del names[:]
 
204
  names.extend(new_names)
193
205
 
 
206
new_data_files = []
194
207
for file_ in data_files:
195
 
  if os.path.isdir(file_['srcpath']):
196
 
    os.path.walk(file_['srcpath'], add, [file_['mode'], file_['srcpath'], file_['dstpath']])
197
 
data_files = filter(lambda file_: not os.path.isdir(file_['srcpath']), data_files)
 
208
  if not should_ignore(file_['srcpath']):
 
209
    if os.path.isdir(file_['srcpath']):
 
210
      os.path.walk(file_['srcpath'], add, [file_['mode'], file_['srcpath'], file_['dstpath']])
 
211
    else:
 
212
      new_data_files.append(file_)
 
213
data_files = filter(lambda file_: not os.path.isdir(file_['srcpath']), new_data_files)
 
214
if len(data_files) == 0:
 
215
  print >> sys.stderr, 'Nothing to do!' 
 
216
  sys.exit(1)
198
217
 
199
218
# Absolutize paths, and check that they make sense
200
219
curr_abspath = os.path.abspath(os.getcwd())
345
364
      send: function() {},
346
365
      onload: function() {
347
366
        var byteArray = this.byteArray.subarray(this.start, this.end);
348
 
        if (this.crunched) {
349
 
          var ddsHeader = byteArray.subarray(0, 128);
350
 
          var that = this;
351
 
          requestDecrunch(this.name, byteArray.subarray(128), function(ddsData) {
352
 
            byteArray = new Uint8Array(ddsHeader.length + ddsData.length);
353
 
            byteArray.set(ddsHeader, 0);
354
 
            byteArray.set(ddsData, 128);
355
 
            that.finish(byteArray);
356
 
          });
357
 
        } else {
 
367
%s
358
368
          this.finish(byteArray);
359
 
        }
 
369
%s
360
370
      },
361
371
      finish: function(byteArray) {
362
372
        var that = this;
366
376
          if (that.audio) {
367
377
            Module['removeRunDependency']('fp ' + that.name); // workaround for chromium bug 124926 (still no audio with this, but at least we don't hang)
368
378
          } else {
369
 
            Runtime.warn('Preloading file ' + that.name + ' failed');
 
379
            Module.printErr('Preloading file ' + that.name + ' failed');
370
380
          }
371
381
        }, false, true); // canOwn this data in the filesystem, it is a slide into the heap that will never change
372
382
        this.requests[this.name] = null;
373
383
      },
374
384
    };
375
 
  '''
 
385
  ''' % ('' if not crunch else '''
 
386
        if (this.crunched) {
 
387
          var ddsHeader = byteArray.subarray(0, 128);
 
388
          var that = this;
 
389
          requestDecrunch(this.name, byteArray.subarray(128), function(ddsData) {
 
390
            byteArray = new Uint8Array(ddsHeader.length + ddsData.length);
 
391
            byteArray.set(ddsHeader, 0);
 
392
            byteArray.set(ddsData, 128);
 
393
            that.finish(byteArray);
 
394
          });
 
395
        } else {
 
396
''', '' if not crunch else '''
 
397
        }
 
398
''')
376
399
 
377
400
counter = 0
378
401
for file_ in data_files:
412
435
 
413
436
if has_preloaded:
414
437
  # Get the big archive and split it up
415
 
  use_data = '''
 
438
  if no_heap_copy:
 
439
    use_data = '''
416
440
      // copy the entire loaded file into a spot in the heap. Files will refer to slices in that. They cannot be freed though.
417
441
      var ptr = Module['_malloc'](byteArray.length);
418
442
      Module['HEAPU8'].set(byteArray, ptr);
419
443
      DataRequest.prototype.byteArray = Module['HEAPU8'].subarray(ptr, ptr+byteArray.length);
420
444
'''
 
445
  else:
 
446
    use_data = '''
 
447
      // Reuse the bytearray from the XHR as the source for file reads.
 
448
      DataRequest.prototype.byteArray = byteArray;
 
449
'''
421
450
  for file_ in data_files:
422
451
    if file_['mode'] == 'preload':
423
452
      use_data += '          DataRequest.prototype.requests["%s"].onload();\n' % (file_['dstpath'])
432
461
    ''' % use_data
433
462
 
434
463
  package_uuid = uuid.uuid4();
 
464
  remote_package_name = os.path.basename(Compression.compressed_name(data_target) if Compression.on else data_target)
435
465
  code += r'''
436
 
    if (!Module.expectedDataFileDownloads) {
437
 
      Module.expectedDataFileDownloads = 0;
438
 
      Module.finishedDataFileDownloads = 0;
 
466
    var PACKAGE_PATH;
 
467
    if (typeof window === 'object') {
 
468
      PACKAGE_PATH = window['encodeURIComponent'](window.location.pathname.toString().substring(0, window.location.pathname.toString().lastIndexOf('/')) + '/');
 
469
    } else {
 
470
      // worker
 
471
      PACKAGE_PATH = encodeURIComponent(location.pathname.toString().substring(0, location.pathname.toString().lastIndexOf('/')) + '/');
439
472
    }
440
 
    Module.expectedDataFileDownloads++;
441
 
 
442
 
    var PACKAGE_PATH = window['encodeURIComponent'](window.location.pathname.toString().substring(0, window.location.pathname.toString().lastIndexOf('/')) + '/');
443
473
    var PACKAGE_NAME = '%s';
444
474
    var REMOTE_PACKAGE_NAME = '%s';
445
475
    var PACKAGE_UUID = '%s';
446
 
  ''' % (data_target, os.path.basename(Compression.compressed_name(data_target) if Compression.on else data_target), package_uuid)
 
476
  ''' % (data_target, remote_package_name, package_uuid)
447
477
 
448
478
  if use_preload_cache:
449
479
    code += r'''
536
566
      };
537
567
    '''
538
568
 
539
 
  code += r'''
 
569
  ret += r'''
540
570
    function fetchRemotePackage(packageName, callback, errback) {
541
571
      var xhr = new XMLHttpRequest();
542
572
      xhr.open('GET', packageName, true);
564
594
            num++;
565
595
          }
566
596
          total = Math.ceil(total * Module.expectedDataFileDownloads/num);
567
 
          Module['setStatus']('Downloading data... (' + loaded + '/' + total + ')');
 
597
          if (Module['setStatus']) Module['setStatus']('Downloading data... (' + loaded + '/' + total + ')');
568
598
        } else if (!Module.dataFileDownloads) {
569
 
          Module['setStatus']('Downloading data...');
 
599
          if (Module['setStatus']) Module['setStatus']('Downloading data...');
570
600
        }
571
601
      };
572
602
      xhr.onload = function(event) {
576
606
      xhr.send(null);
577
607
    };
578
608
 
 
609
    function handleError(error) {
 
610
      console.error('package error:', error);
 
611
    };
 
612
  '''
 
613
 
 
614
  code += r'''
579
615
    function processPackageData(arrayBuffer) {
580
616
      Module.finishedDataFileDownloads++;
581
617
      assert(arrayBuffer, 'Loading data file failed.');
584
620
      %s
585
621
    };
586
622
    Module['addRunDependency']('datafile_%s');
587
 
 
588
 
    function handleError(error) {
589
 
      console.error('package error:', error);
590
 
    };
591
623
  ''' % (use_data, data_target) # use basename because from the browser's point of view, we need to find the datafile in the same dir as the html file
592
624
 
593
625
  code += r'''
594
 
    if (!Module.preloadResults)
595
 
      Module.preloadResults = {};
 
626
    if (!Module.preloadResults) Module.preloadResults = {};
596
627
  '''
597
628
 
598
629
  if use_preload_cache:
631
662
      if (Module['setStatus']) Module['setStatus']('Downloading...');
632
663
    '''
633
664
  else:
 
665
    # Not using preload cache, so we might as well start the xhr ASAP, potentially before JS parsing of the main codebase if it's after us.
 
666
    # Only tricky bit is the fetch is async, but also when runWithFS is called is async, so we handle both orderings.
 
667
    ret += r'''
 
668
      var fetched = null, fetchedCallback = null;
 
669
      fetchRemotePackage('%s', function(data) {
 
670
        if (fetchedCallback) {
 
671
          fetchedCallback(data);
 
672
          fetchedCallback = null;
 
673
        } else {
 
674
          fetched = data;
 
675
        }
 
676
      }, handleError);
 
677
    ''' % os.path.basename(Compression.compressed_name(data_target) if Compression.on else data_target)
 
678
 
634
679
    code += r'''
635
680
      Module.preloadResults[PACKAGE_NAME] = {fromCache: false};
636
 
      fetchRemotePackage(REMOTE_PACKAGE_NAME, processPackageData, handleError);
 
681
      if (fetched) {
 
682
        processPackageData(fetched);
 
683
        fetched = null;
 
684
      } else {
 
685
        fetchedCallback = processPackageData;
 
686
      }
637
687
    '''
638
688
 
639
 
if pre_run:
640
 
  ret += '''
641
 
  if (typeof Module == 'undefined') Module = {};
642
 
  if (!Module['preRun']) Module['preRun'] = [];
643
 
  Module["preRun"].push(function() {
 
689
ret += '''
 
690
  function runWithFS() {
644
691
'''
645
692
ret += code
646
 
 
647
 
if pre_run:
648
 
  ret += '  });\n'
 
693
ret += '''
 
694
  }
 
695
  if (Module['calledRun']) {
 
696
    runWithFS();
 
697
  } else {
 
698
    if (!Module['preRun']) Module['preRun'] = [];
 
699
    Module["preRun"].push(runWithFS); // FS is not initialized yet, wait for it
 
700
  }
 
701
'''
649
702
 
650
703
if crunch:
651
704
  ret += '''