libjxl

FORK: libjxl patches used on blog
git clone https://git.neptards.moe/blog/libjxl.git
Log | Files | Refs | Submodules | README | LICENSE

build_site.py (4189B)


      1 #!/usr/bin/env python3
      2 # Copyright (c) the JPEG XL Project Authors. All rights reserved.
      3 #
      4 # Use of this source code is governed by a BSD-style
      5 # license that can be found in the LICENSE file.
      6 
      7 import shutil
      8 import subprocess
      9 import sys
     10 
     11 from pathlib import Path
     12 
     13 BROTLIFY = False
     14 ZOPFLIFY = False
     15 LEAN = True
     16 NETLIFY = False
     17 
     18 REMOVE_SHEBANG = ['jxl_decoder.js']
     19 EMBED_BIN = [
     20   'jxl_decoder.js',
     21   'jxl_decoder.worker.js'
     22 ]
     23 EMBED_SRC = ['client_worker.js']
     24 TEMPLATES = ['service_worker.js']
     25 COPY_BIN = ['jxl_decoder.wasm'] + [] if LEAN else EMBED_BIN
     26 COPY_SRC = [
     27   'one_line_demo.html',
     28   'one_line_demo_with_console.html',
     29   'manual_decode_demo.html',
     30 ] + [] if not NETLIFY else [
     31   'netlify.toml',
     32   'netlify'
     33 ] + [] if LEAN else EMBED_SRC
     34 
     35 COMPRESS = COPY_BIN + COPY_SRC + TEMPLATES
     36 COMPRESSIBLE_EXT = ['.html', '.js', '.wasm']
     37 
     38 def escape_js(js):
     39   return js.replace('\\', '\\\\').replace('\'', '\\\'')
     40 
     41 def remove_shebang(txt):
     42   lines = txt.splitlines(True) # Keep line-breaks
     43   if len(lines) > 0:
     44     if lines[0].startswith('#!'):
     45       lines = lines[1:]
     46   return ''.join(lines)
     47 
     48 def compress(path):
     49   name = path.name
     50   compressible = any([name.endswith(ext) for ext in COMPRESSIBLE_EXT])
     51   if not compressible:
     52     print(f'Not compressing {name}')
     53     return
     54   print(f'Processing {name}')
     55   orig_size = path.stat().st_size
     56   if BROTLIFY:
     57     cmd_brotli = ['brotli', '-Zfk', path.absolute()]
     58     subprocess.run(cmd_brotli, check=True, stdout=sys.stdout, stderr=sys.stderr)
     59     br_size = path.parent.joinpath(name + '.br').stat().st_size
     60     print(f'  Brotli: {orig_size} -> {br_size}')
     61   if ZOPFLIFY:
     62     cmd_zopfli = ['zopfli', path.absolute()]
     63     subprocess.run(cmd_zopfli, check=True, stdout=sys.stdout, stderr=sys.stderr)
     64     gz_size = path.parent.joinpath(name + '.gz').stat().st_size
     65     print(f'  Zopfli: {orig_size} -> {gz_size}')
     66 
     67 def check_util(name):
     68   cmd = [name, '-h']
     69   try:
     70     subprocess.run(cmd, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
     71   except:
     72     print(f"NOTE: {name} not installed")
     73     return False
     74   return True
     75 
     76 def check_utils():
     77   global BROTLIFY
     78   BROTLIFY = BROTLIFY and check_util('brotli')
     79   global ZOPFLIFY
     80   ZOPFLIFY = ZOPFLIFY and check_util('zopfli')
     81   if not check_util('uglifyjs'):
     82     print("FAIL: uglifyjs is required to build a site")
     83     sys.exit()
     84 
     85 def uglify(text, name):
     86   cmd = ['uglifyjs', '-m', '-c']
     87   ugly_result = subprocess.run(
     88       cmd, capture_output=True, check=True, input=text, text=True)
     89   ugly_text = ugly_result.stdout.strip()
     90   print(f'Uglify {name}: {len(text)} -> {len(ugly_text)}')
     91   return ugly_text
     92 
     93 if __name__ == "__main__":
     94   if len(sys.argv) != 4:
     95     print(f"Usage: python3 {sys.argv[0]} SRC_DIR BINARY_DIR OUTPUT_DIR")
     96     exit(-1)
     97   source_path = Path(sys.argv[1]) # CMake build dir
     98   binary_path = Path(sys.argv[2]) # Site template dir
     99   output_path = Path(sys.argv[3]) # Site output
    100 
    101   check_utils()
    102 
    103   for name in REMOVE_SHEBANG:
    104     path = binary_path.joinpath(name)
    105     text = path.read_text().strip()
    106     path.write_text(remove_shebang(text))
    107     remove_shebang
    108 
    109   substitutes = {}
    110 
    111   for name in EMBED_BIN:
    112     key = '$' + name + '$'
    113     path = binary_path.joinpath(name)
    114     value = escape_js(uglify(path.read_text().strip(), name))
    115     substitutes[key] = value
    116 
    117   for name in EMBED_SRC:
    118     key = '$' + name + '$'
    119     path = source_path.joinpath(name)
    120     value = escape_js(uglify(path.read_text().strip(), name))
    121     substitutes[key] = value
    122 
    123   for name in TEMPLATES:
    124     print(f'Processing template {name}')
    125     path = source_path.joinpath(name)
    126     text = path.read_text().strip()
    127     for key, value in substitutes.items():
    128       text = text.replace(key, value)
    129     #text = uglify(text, name)
    130     output_path.joinpath(name).write_text(text)
    131 
    132   for name in COPY_SRC:
    133     path = source_path.joinpath(name)
    134     if path.is_dir():
    135       shutil.copytree(path, output_path.joinpath(
    136           name).absolute(), dirs_exist_ok=True)
    137     else:
    138       shutil.copy(path, output_path.absolute())
    139 
    140   # TODO(eustas): uglify
    141   for name in COPY_BIN:
    142     shutil.copy(binary_path.joinpath(name), output_path.absolute())
    143 
    144   for name in COMPRESS:
    145     compress(output_path.joinpath(name))