~ubuntu-branches/ubuntu/vivid/emscripten/vivid

« back to all changes in this revision

Viewing changes to tools/file_packager.py

  • Committer: Package Import Robot
  • Author(s): Sylvestre Ledru
  • Date: 2013-09-20 22:44:35 UTC
  • mfrom: (4.1.1 sid)
  • Revision ID: package-import@ubuntu.com-20130920224435-apuwj4fsl3fqv1a6
Tags: 1.5.6~20130920~6010666-1
* New snapshot release
* Update the list of supported architectures to the same as libv8
  (Closes: #723129)
* emlibtool has been removed from upstream.
* Fix warning syntax-error-in-dep5-copyright
* Refresh of the patches

Show diffs side-by-side

added added

removed removed

Lines of Context:
39
39
             to dds files in the browser, exactly the same as if this tool compressed them.
40
40
'''
41
41
 
42
 
import os, sys, shutil, random, uuid
43
 
 
 
42
import os, sys, shutil, random, uuid, ctypes
 
43
import posixpath
44
44
import shared
45
45
from shared import Compression, execute, suffix, unsuffixed
46
46
from subprocess import Popen, PIPE, STDOUT
50
50
See the source for more details.'''
51
51
  sys.exit(0)
52
52
 
 
53
DEBUG = os.environ.get('EMCC_DEBUG')
 
54
 
53
55
data_target = sys.argv[1]
54
56
 
55
57
IMAGE_SUFFIXES = ('.jpg', '.png', '.bmp')
120
122
      srcpath, dstpath = arg.split('@') # User is specifying destination filename explicitly.
121
123
    else:
122
124
      srcpath = dstpath = arg # Use source path as destination path.
123
 
      if os.path.isabs(dstpath):
124
 
        print >> sys.stderr, 'Warning: Embedding an absolute file/directory name "' + dstpath + '" to the virtual filesystem. The file will be made available in the path "' + dstpath + '", and not in the root of the generated file system. Use the explicit syntax --preload-file srcpath@dstpath to specify the target location the absolute source path should be directed to.'
125
125
    if os.path.isfile(srcpath) or os.path.isdir(srcpath):
126
126
      data_files.append({ 'srcpath': srcpath, 'dstpath': dstpath, 'mode': mode })
127
127
    else:
150
150
}
151
151
'''
152
152
 
 
153
# Win32 code to test whether the given file has the hidden property set.
 
154
def has_hidden_attribute(filepath):
 
155
  if sys.platform != 'win32':
 
156
    return False
 
157
    
 
158
  try:
 
159
    attrs = ctypes.windll.kernel32.GetFileAttributesW(unicode(filepath))
 
160
    assert attrs != -1
 
161
    result = bool(attrs & 2)
 
162
  except (AttributeError, AssertionError):
 
163
    result = False
 
164
  return result
 
165
 
 
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):
 
171
    return True
 
172
    
 
173
  components = filename.replace('\\\\', '/').replace('\\', '/').split('/')
 
174
  for c in components:
 
175
    if c.startswith('.') and c != '.' and c != '..':
 
176
      return True
 
177
  return False
 
178
 
153
179
# Expand directories into individual files
154
180
def add(arg, dirname, names):
155
181
  # rootpathsrc: The path name of the root directory on the local FS we are adding to emscripten virtual FS.
158
184
  for name in names:
159
185
    fullname = os.path.join(dirname, name)
160
186
    if not os.path.isdir(fullname):
161
 
      dstpath = os.path.join(rootpathdst, os.path.relpath(fullname, rootpathsrc)) # Convert source filename relative to root directory of target FS.
162
 
      data_files.append({ 'srcpath': fullname, 'dstpath': dstpath, 'mode': mode })
 
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:
 
191
        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 })
163
193
 
164
194
for file_ in data_files:
165
195
  if os.path.isdir(file_['srcpath']):
166
196
    os.path.walk(file_['srcpath'], add, [file_['mode'], file_['srcpath'], file_['dstpath']])
167
197
data_files = filter(lambda file_: not os.path.isdir(file_['srcpath']), data_files)
168
198
 
 
199
# Absolutize paths, and check that they make sense
 
200
curr_abspath = os.path.abspath(os.getcwd())
 
201
for file_ in data_files:
 
202
  if file_['srcpath'] == file_['dstpath']:
 
203
    # This file was not defined with src@dst, so we inferred the destination from the source. In that case,
 
204
    # we require that the destination not be under the current location
 
205
    path = file_['dstpath']
 
206
    abspath = os.path.abspath(path)
 
207
    if DEBUG: print >> sys.stderr, path, abspath, curr_abspath
 
208
    if not abspath.startswith(curr_abspath):
 
209
      print >> sys.stderr, 'Error: Embedding "%s" which is below the current directory "%s". This is invalid since the current directory becomes the root that the generated code will see' % (path, curr_abspath)
 
210
      sys.exit(1)
 
211
    file_['dstpath'] = abspath[len(curr_abspath)+1:]
 
212
    if os.path.isabs(path):
 
213
      print >> sys.stderr, 'Warning: Embedding an absolute file/directory name "' + path + '" to the virtual filesystem. The file will be made available in the relative path "' + file_['dstpath'] + '". You can use the explicit syntax --preload-file srcpath@dstpath to explicitly specify the target location the absolute source path should be directed to.'
 
214
 
169
215
for file_ in data_files:
170
216
  file_['dstpath'] = file_['dstpath'].replace(os.path.sep, '/') # name in the filesystem, native and emulated
171
217
  if file_['dstpath'].endswith('/'): # If user has submitted a directory name as the destination but omitted the destination filename, use the filename from source file
172
218
    file_['dstpath'] = file_['dstpath'] + os.path.basename(file_['srcpath'])
173
 
  if file_['dstpath'].startswith('./'): file_['dstpath'] = file_['dstpath'][2:] # remove redundant ./ prefix
 
219
  # make destination path always relative to the root
 
220
  file_['dstpath'] = posixpath.normpath(os.path.join('/', file_['dstpath']))
 
221
  if DEBUG:
 
222
    print >> sys.stderr, 'Packaging file "' + file_['srcpath'] + '" to VFS in path "' + file_['dstpath'] + '".'
174
223
 
175
224
# Remove duplicates (can occur naively, for example preload dir/, preload dir/subdir/)
176
225
seen = {}
202
251
    function requestDecrunch(filename, data, callback) {
203
252
      decrunchWorker.postMessage({
204
253
        filename: filename,
205
 
        data: data,
 
254
        data: new Uint8Array(data),
206
255
        callbackID: decrunchCallbacks.length
207
256
      });
208
257
      decrunchCallbacks.push(callback);
280
329
 
281
330
  # Data requests - for getting a block of data out of the big archive - have a similar API to XHRs
282
331
  code += '''
283
 
    function DataRequest() {}
 
332
    function DataRequest(start, end, crunched, audio) {
 
333
      this.start = start;
 
334
      this.end = end;
 
335
      this.crunched = crunched;
 
336
      this.audio = audio;
 
337
    }
284
338
    DataRequest.prototype = {
285
339
      requests: {},
286
340
      open: function(mode, name) {
 
341
        this.name = name;
287
342
        this.requests[name] = this;
288
 
      },
289
 
      send: function() {}
 
343
        Module['addRunDependency']('fp ' + this.name);
 
344
      },
 
345
      send: function() {},
 
346
      onload: function() {
 
347
        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 {
 
358
          this.finish(byteArray);
 
359
        }
 
360
      },
 
361
      finish: function(byteArray) {
 
362
        var that = this;
 
363
        Module['FS_createPreloadedFile'](this.name, null, byteArray, true, true, function() {
 
364
          Module['removeRunDependency']('fp ' + that.name);
 
365
        }, function() {
 
366
          if (that.audio) {
 
367
            Module['removeRunDependency']('fp ' + that.name); // workaround for chromium bug 124926 (still no audio with this, but at least we don't hang)
 
368
          } else {
 
369
            Runtime.warn('Preloading file ' + that.name + ' failed');
 
370
          }
 
371
        }, false, true); // canOwn this data in the filesystem, it is a slide into the heap that will never change
 
372
        this.requests[this.name] = null;
 
373
      },
290
374
    };
291
375
  '''
292
376
 
293
377
counter = 0
294
378
for file_ in data_files:
295
379
  filename = file_['dstpath']
 
380
  dirname = os.path.dirname(filename)
 
381
  basename = os.path.basename(filename)
296
382
  if file_['mode'] == 'embed':
297
383
    # Embed
298
384
    data = map(ord, open(file_['srcpath'], 'rb').read())
299
 
    str_data = ''
300
 
    chunk_size = 10240
301
 
    while len(data) > 0:
302
 
      chunk = data[:chunk_size]
303
 
      data = data[chunk_size:]
304
 
      if not str_data:
305
 
        str_data = str(chunk)
306
 
      else:
307
 
        str_data += '.concat(' + str(chunk) + ')'
308
 
    code += '''Module['FS_createDataFile']('/%s', '%s', %s, true, true);\n''' % (os.path.dirname(filename), os.path.basename(filename), str_data)
 
385
    if not data:
 
386
      str_data = '[]'
 
387
    else:
 
388
      str_data = ''
 
389
      chunk_size = 10240
 
390
      while len(data) > 0:
 
391
        chunk = data[:chunk_size]
 
392
        data = data[chunk_size:]
 
393
        if not str_data:
 
394
          str_data = str(chunk)
 
395
        else:
 
396
          str_data += '.concat(' + str(chunk) + ')'
 
397
    code += '''Module['FS_createDataFile']('%s', '%s', %s, true, true);\n''' % (dirname, basename, str_data)
309
398
  elif file_['mode'] == 'preload':
310
399
    # Preload
311
400
    varname = 'filePreload%d' % counter
312
401
    counter += 1
313
 
    dds = crunch and filename.endswith(CRUNCH_INPUT_SUFFIX)
314
 
 
315
 
    prepare = ''
316
 
    finish = "Module['removeRunDependency']('fp %s');\n" % filename
317
 
 
318
 
    if dds:
319
 
      # decompress crunch format into dds
320
 
      prepare = '''
321
 
        var ddsHeader = byteArray.subarray(0, %(dds_header_size)d);
322
 
        requestDecrunch('%(filename)s', byteArray.subarray(%(dds_header_size)d), function(ddsData) {
323
 
          byteArray = new Uint8Array(ddsHeader.length + ddsData.length);
324
 
          byteArray.set(ddsHeader, 0);
325
 
          byteArray.set(ddsData, %(dds_header_size)d);
326
 
''' % { 'filename': filename, 'dds_header_size': DDS_HEADER_SIZE }
327
 
 
328
 
      finish += '''
329
 
        });
330
 
'''
331
 
 
332
 
    code += '''
333
 
    var %(varname)s = new %(request)s();
334
 
    %(varname)s.open('GET', '%(filename)s', true);
335
 
    %(varname)s.responseType = 'arraybuffer';
336
 
    %(varname)s.onload = function() {
337
 
      var arrayBuffer = %(varname)s.response;
338
 
      assert(arrayBuffer, 'Loading file %(filename)s failed.');
339
 
      var byteArray = !arrayBuffer.subarray ? new Uint8Array(arrayBuffer) : arrayBuffer;
340
 
      %(prepare)s
341
 
      Module['FS_createPreloadedFile']('/%(dirname)s', '%(basename)s', byteArray, true, true, function() {
342
 
        %(finish)s
343
 
      }%(fail)s);
344
 
    };
345
 
    Module['addRunDependency']('fp %(filename)s');
346
 
    %(varname)s.send(null);
 
402
    code += '''    new DataRequest(%(start)d, %(end)d, %(crunched)s, %(audio)s).open('GET', '%(filename)s');
347
403
''' % {
348
 
        'request': 'DataRequest', # In the past we also supported XHRs here
349
 
        'varname': varname,
350
 
        'filename': filename,
351
 
        'dirname': os.path.dirname(filename),
352
 
        'basename': os.path.basename(filename),
353
 
        'prepare': prepare,
354
 
        'finish': finish,
355
 
        'fail': '' if filename[-4:] not in AUDIO_SUFFIXES else ''', function() { Module['removeRunDependency']('fp %s') }''' % filename # workaround for chromium bug 124926 (still no audio with this, but at least we don't hang)
356
 
  }
 
404
      'filename': file_['dstpath'],
 
405
      'start': file_['data_start'],
 
406
      'end': file_['data_end'],
 
407
      'crunched': '1' if crunch and filename.endswith(CRUNCH_INPUT_SUFFIX) else '0',
 
408
      'audio': '1' if filename[-4:] in AUDIO_SUFFIXES else '0',
 
409
    }
357
410
  else:
358
411
    assert 0
359
412
 
360
413
if has_preloaded:
361
414
  # Get the big archive and split it up
362
 
  use_data = ''
 
415
  use_data = '''
 
416
      // copy the entire loaded file into a spot in the heap. Files will refer to slices in that. They cannot be freed though.
 
417
      var ptr = Module['_malloc'](byteArray.length);
 
418
      Module['HEAPU8'].set(byteArray, ptr);
 
419
      DataRequest.prototype.byteArray = Module['HEAPU8'].subarray(ptr, ptr+byteArray.length);
 
420
'''
363
421
  for file_ in data_files:
364
422
    if file_['mode'] == 'preload':
365
 
      use_data += '''
366
 
        curr = DataRequest.prototype.requests['%s'];
367
 
        var data = byteArray.subarray(%d, %d);
368
 
        var ptr = Module['_malloc'](%d);
369
 
        Module['HEAPU8'].set(data, ptr);
370
 
        curr.response = Module['HEAPU8'].subarray(ptr, ptr + %d);
371
 
        curr.onload();
372
 
      ''' % (file_['dstpath'], file_['data_start'], file_['data_end'], file_['data_end'] - file_['data_start'], file_['data_end'] - file_['data_start'])
 
423
      use_data += '          DataRequest.prototype.requests["%s"].onload();\n' % (file_['dstpath'])
373
424
  use_data += "          Module['removeRunDependency']('datafile_%s');\n" % data_target
374
425
 
375
426
  if Compression.on: