Skip to content

Commit fc89621

Browse files
committed
file_packager.py: Round to cleanups and refactorings. NFC
The biggest change here is the of a DataFile class over raw dict. This simplifies field access and makes things more explicit. I tried to use `namedtuple` here but we need to write to at least one of the fields.
1 parent 1e92e6c commit fc89621

File tree

1 file changed

+82
-74
lines changed

1 file changed

+82
-74
lines changed

tools/file_packager.py

Lines changed: 82 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -58,22 +58,22 @@
5858
"""
5959

6060
import base64
61+
import ctypes
62+
import fnmatch
63+
import json
6164
import os
62-
import sys
63-
import shutil
65+
import posixpath
6466
import random
67+
import shutil
68+
import sys
6569
import uuid
66-
import ctypes
70+
from subprocess import PIPE
6771

6872
__scriptdir__ = os.path.dirname(os.path.abspath(__file__))
6973
__rootdir__ = os.path.dirname(__scriptdir__)
7074
sys.path.append(__rootdir__)
7175

72-
import posixpath
7376
from tools import shared, utils, js_manipulation
74-
from subprocess import PIPE
75-
import fnmatch
76-
import json
7777

7878
if len(sys.argv) == 1:
7979
print('''Usage: file_packager TARGET [--preload A [B..]] [--embed C [D..]] [--exclude E [F..]]] [--js-output=OUTPUT.js] [--no-force] [--use-preload-cache] [--indexedDB-name=EM_PRELOAD_CACHE] [--separate-metadata] [--lz4] [--use-preload-plugins]
@@ -98,6 +98,39 @@
9898
new_data_files = []
9999

100100

101+
class Options:
102+
def __init__(self):
103+
self.export_name = 'Module'
104+
self.has_preloaded = False
105+
self.jsoutput = None
106+
self.from_emcc = False
107+
self.force = True
108+
# If set to True, IndexedDB (IDBFS in library_idbfs.js) is used to locally
109+
# cache VFS XHR so that subsequent page loads can read the data from the
110+
# offline cache instead.
111+
self.use_preload_cache = False
112+
self.indexeddb_name = 'EM_PRELOAD_CACHE'
113+
# If set to True, the package metadata is stored separately from js-output
114+
# file which makes js-output file immutable to the package content changes.
115+
# If set to False, the package metadata is stored inside the js-output file
116+
# which makes js-output file to mutate on each invocation of this packager tool.
117+
self.separate_metadata = False
118+
self.lz4 = False
119+
self.use_preload_plugins = False
120+
self.support_node = True
121+
122+
123+
class DataFile:
124+
def __init__(self, srcpath, dstpath, mode, explicit_dst_path):
125+
self.srcpath = srcpath
126+
self.dstpath = dstpath
127+
self.mode = mode
128+
self.explicit_dst_path = explicit_dst_path
129+
130+
131+
options = Options()
132+
133+
101134
def base64_encode(b):
102135
b64 = base64.b64encode(b)
103136
return b64.decode('ascii')
@@ -154,48 +187,22 @@ def add(mode, rootpathsrc, rootpathdst):
154187
# Convert source filename relative to root directory of target FS.
155188
dstpath = os.path.join(rootpathdst,
156189
os.path.relpath(fullname, rootpathsrc))
157-
new_data_files.append({'srcpath': fullname, 'dstpath': dstpath,
158-
'mode': mode, 'explicit_dst_path': True})
190+
new_data_files.append(DataFile(srcpath=fullname, dstpath=dstpath,
191+
mode=mode, explicit_dst_path=True))
159192
elif DEBUG:
160193
print('Skipping file "%s" from inclusion in the emscripten '
161194
'virtual file system.' % fullname, file=sys.stderr)
162195
dirnames.clear()
163196
dirnames.extend(new_dirnames)
164197

165198

166-
class Options:
167-
def __init__(self):
168-
self.export_name = 'Module'
169-
self.has_preloaded = False
170-
self.jsoutput = None
171-
self.from_emcc = False
172-
self.force = True
173-
# If set to True, IndexedDB (IDBFS in library_idbfs.js) is used to locally
174-
# cache VFS XHR so that subsequent page loads can read the data from the
175-
# offline cache instead.
176-
self.use_preload_cache = False
177-
self.indexeddb_name = 'EM_PRELOAD_CACHE'
178-
# If set to True, the package metadata is stored separately from js-output
179-
# file which makes js-output file immutable to the package content changes.
180-
# If set to False, the package metadata is stored inside the js-output file
181-
# which makes js-output file to mutate on each invocation of this packager tool.
182-
self.separate_metadata = False
183-
self.lz4 = False
184-
self.use_preload_plugins = False
185-
self.support_node = True
186-
187-
188-
options = Options()
189-
190-
191199
def main():
192200
data_files = []
193201
plugins = []
194202
leading = ''
195203

196204
for arg in sys.argv[2:]:
197205
if arg == '--preload':
198-
options.has_preloaded = True
199206
leading = 'preload'
200207
elif arg == '--embed':
201208
leading = 'embed'
@@ -256,8 +263,8 @@ def main():
256263
# Use source path as destination path.
257264
srcpath = dstpath = arg.replace('@@', '@')
258265
if os.path.isfile(srcpath) or os.path.isdir(srcpath):
259-
data_files.append({'srcpath': srcpath, 'dstpath': dstpath, 'mode': mode,
260-
'explicit_dst_path': uses_at_notation})
266+
data_files.append(DataFile(srcpath=srcpath, dstpath=dstpath, mode=mode,
267+
explicit_dst_path=uses_at_notation))
261268
else:
262269
print('error: ' + arg + ' does not exist', file=sys.stderr)
263270
return 1
@@ -267,12 +274,13 @@ def main():
267274
print('Unknown parameter:', arg, file=sys.stderr)
268275
return 1
269276

270-
if (not options.force) and not data_files:
271-
options.has_preloaded = False
272-
if not options.has_preloaded or options.jsoutput is None:
273-
assert not options.separate_metadata, (
274-
'cannot separate-metadata without both --preloaded files '
275-
'and a specified --js-output')
277+
options.has_preloaded = any(f.mode == 'preload' for f in data_files)
278+
279+
if options.separate_metadata:
280+
if not options.has_preloaded or not options.jsoutput:
281+
print('cannot separate-metadata without both --preloaded files '
282+
'and a specified --js-output')
283+
return 1
276284

277285
if not options.from_emcc:
278286
print('Remember to build the main file with -s FORCE_FILESYSTEM=1 '
@@ -285,13 +293,13 @@ def main():
285293
return 1
286294

287295
for file_ in data_files:
288-
if not should_ignore(file_['srcpath']):
289-
if os.path.isdir(file_['srcpath']):
290-
add(file_['mode'], file_['srcpath'], file_['dstpath'])
296+
if not should_ignore(file_.srcpath):
297+
if os.path.isdir(file_.srcpath):
298+
add(file_.mode, file_.srcpath, file_.dstpath)
291299
else:
292300
new_data_files.append(file_)
293301
data_files = [file_ for file_ in new_data_files
294-
if not os.path.isdir(file_['srcpath'])]
302+
if not os.path.isdir(file_.srcpath)]
295303
if len(data_files) == 0:
296304
print('Nothing to do!', file=sys.stderr)
297305
sys.exit(1)
@@ -301,55 +309,55 @@ def main():
301309
# even if we cd'd into a symbolic link.
302310
curr_abspath = os.path.abspath(os.getcwd())
303311

304-
for file_ in data_files:
305-
if not file_['explicit_dst_path']:
312+
if not file_.explicit_dst_path:
313+
for file_ in data_files:
306314
# This file was not defined with src@dst, so we inferred the destination
307315
# from the source. In that case, we require that the destination not be
308316
# under the current location
309-
path = file_['dstpath']
317+
path = file_.dstpath
310318
# Use os.path.realpath to resolve any symbolic links to hard paths,
311319
# to match the structure in curr_abspath.
312320
abspath = os.path.realpath(os.path.abspath(path))
313321
if DEBUG:
314-
print(path, abspath, curr_abspath, file=sys.stderr)
322+
print(path, abspath, curr_abspath, file=sys.stderr)
315323
if not abspath.startswith(curr_abspath):
316324
print('Error: Embedding "%s" which is below the current directory '
317325
'"%s". This is invalid since the current directory becomes the '
318326
'root that the generated code will see' % (path, curr_abspath),
319327
file=sys.stderr)
320328
sys.exit(1)
321-
file_['dstpath'] = abspath[len(curr_abspath) + 1:]
329+
file_.dstpath = abspath[len(curr_abspath) + 1:]
322330
if os.path.isabs(path):
323331
print('Warning: Embedding an absolute file/directory name "%s" to the '
324332
'virtual filesystem. The file will be made available in the '
325333
'relative path "%s". You can use the explicit syntax '
326334
'--preload-file srcpath@dstpath to explicitly specify the target '
327335
'location the absolute source path should be directed to.'
328-
% (path, file_['dstpath']), file=sys.stderr)
336+
% (path, file_.dstpath), file=sys.stderr)
329337

330338
for file_ in data_files:
331339
# name in the filesystem, native and emulated
332-
file_['dstpath'] = file_['dstpath'].replace(os.path.sep, '/')
340+
file_.dstpath = file_.dstpath.replace(os.path.sep, '/')
333341
# If user has submitted a directory name as the destination but omitted
334342
# the destination filename, use the filename from source file
335-
if file_['dstpath'].endswith('/'):
336-
file_['dstpath'] = file_['dstpath'] + os.path.basename(file_['srcpath'])
343+
if file_.dstpath.endswith('/'):
344+
file_.dstpath = file_.dstpath + os.path.basename(file_.srcpath)
337345
# make destination path always relative to the root
338-
file_['dstpath'] = posixpath.normpath(os.path.join('/', file_['dstpath']))
346+
file_.dstpath = posixpath.normpath(os.path.join('/', file_.dstpath))
339347
if DEBUG:
340348
print('Packaging file "%s" to VFS in path "%s".'
341-
% (file_['srcpath'], file_['dstpath']), file=sys.stderr)
349+
% (file_.srcpath, file_.dstpath), file=sys.stderr)
342350

343351
# Remove duplicates (can occur naively, for example preload dir/, preload dir/subdir/)
344-
seen = {}
352+
seen = set()
345353

346354
def was_seen(name):
347-
if seen.get(name):
348-
return True
349-
seen[name] = 1
355+
if name in seen:
356+
return True
357+
seen.add(name)
350358
return False
351359

352-
data_files = [file_ for file_ in data_files if not was_seen(file_['dstpath'])]
360+
data_files = [file_ for file_ in data_files if not was_seen(file_.dstpath)]
353361

354362
if AV_WORKAROUND:
355363
random.shuffle(data_files)
@@ -414,7 +422,7 @@ def generate_js(data_files, metadata):
414422
# Set up folders
415423
partial_dirs = []
416424
for file_ in data_files:
417-
dirname = os.path.dirname(file_['dstpath'])
425+
dirname = os.path.dirname(file_.dstpath)
418426
dirname = dirname.lstrip('/') # absolute paths start with '/', remove that
419427
if dirname != '':
420428
parts = dirname.split('/')
@@ -431,10 +439,10 @@ def generate_js(data_files, metadata):
431439
start = 0
432440
with open(data_target, 'wb') as data:
433441
for file_ in data_files:
434-
file_['data_start'] = start
435-
with open(file_['srcpath'], 'rb') as f:
442+
file_.data_start = start
443+
with open(file_.srcpath, 'rb') as f:
436444
curr = f.read()
437-
file_['data_end'] = start + len(curr)
445+
file_.data_end = start + len(curr)
438446
if AV_WORKAROUND:
439447
curr += '\x00'
440448
start += len(curr)
@@ -499,21 +507,21 @@ def generate_js(data_files, metadata):
499507
}\n''' % (create_preloaded if options.use_preload_plugins else create_data)
500508

501509
for (counter, file_) in enumerate(data_files):
502-
filename = file_['dstpath']
510+
filename = file_.dstpath
503511
dirname = os.path.dirname(filename)
504512
basename = os.path.basename(filename)
505-
if file_['mode'] == 'embed':
513+
if file_.mode == 'embed':
506514
# Embed
507-
data = base64_encode(utils.read_binary(file_['srcpath']))
515+
data = base64_encode(utils.read_binary(file_.srcpath))
508516
code += " var fileData%d = '%s';\n" % (counter, data)
509517
code += (" Module['FS_createDataFile']('%s', '%s', decodeBase64(fileData%d), true, true, false);\n"
510518
% (dirname, basename, counter))
511-
elif file_['mode'] == 'preload':
519+
elif file_.mode == 'preload':
512520
# Preload
513521
metadata_el = {
514-
'filename': file_['dstpath'],
515-
'start': file_['data_start'],
516-
'end': file_['data_end'],
522+
'filename': file_.dstpath,
523+
'start': file_.data_start,
524+
'end': file_.data_end,
517525
}
518526
if filename[-4:] in AUDIO_SUFFIXES:
519527
metadata_el['audio'] = 1

0 commit comments

Comments
 (0)