201 lines
7.4 KiB
Python
201 lines
7.4 KiB
Python
import shutil
|
||
import gzip
|
||
import os
|
||
import subprocess
|
||
import platform
|
||
from os import popen
|
||
Import("env")
|
||
Import("projenv")
|
||
|
||
def resolve_tool(name):
|
||
local_bin = os.path.join(env['PROJECT_DIR'], 'node_modules', '.bin', name)
|
||
if platform.system() == "Windows":
|
||
local_bin += ".cmd"
|
||
if os.path.exists(local_bin):
|
||
return local_bin
|
||
|
||
found = shutil.which(name)
|
||
if found:
|
||
return found
|
||
|
||
raise FileNotFoundError(f"{name} wurde nicht gefunden. Bitte `npm install` ausführen.")
|
||
|
||
def ensure_npm_dependencies():
|
||
node_modules_bin = os.path.join(env['PROJECT_DIR'], 'node_modules', '.bin')
|
||
required_tools = ["html-minifier", "terser", "cleancss"]
|
||
missing = False
|
||
|
||
for tool in required_tools:
|
||
path = os.path.join(node_modules_bin, tool + ('.cmd' if platform.system() == "Windows" else ''))
|
||
if not os.path.exists(path):
|
||
missing = True
|
||
break
|
||
|
||
if missing:
|
||
print("NPM-Abhängigkeiten fehlen – führe `npm install` aus...")
|
||
subprocess.run(["npm", "install"], cwd=env['PROJECT_DIR'], check=True)
|
||
|
||
|
||
ensure_npm_dependencies()
|
||
|
||
html_minifier_path = resolve_tool("html-minifier")
|
||
terser_path = resolve_tool("terser")
|
||
cleancss_path = resolve_tool("cleancss")
|
||
|
||
def minify_html(input_path, output_path):
|
||
subprocess.run([html_minifier_path, '--collapse-whitespace', '--remove-comments', input_path, '-o', output_path])
|
||
|
||
def minify_js(input_path, output_path):
|
||
subprocess.run([terser_path, input_path, '-o', output_path, '-c', '-m'])
|
||
|
||
def minify_css(input_path, output_path):
|
||
subprocess.run([cleancss_path, '--no-discardUnused', input_path, output_path])
|
||
|
||
def process_file(src_path, dest_path):
|
||
_, file_extension = os.path.splitext(src_path)
|
||
|
||
# Extrahiere den Ordnerpfad im Zielverzeichnis
|
||
dest_dir = os.path.dirname(dest_path)
|
||
|
||
# Erstelle den Ordner und alle dazugehörigen Unterordner, falls sie nicht existieren
|
||
os.makedirs(dest_dir, exist_ok=True)
|
||
|
||
if file_extension.lower() == '.js':
|
||
minify_js(src_path, dest_path)
|
||
elif file_extension.lower() == '.css':
|
||
minify_css(src_path, dest_path)
|
||
elif file_extension.lower() in ['.html', '.htm']:
|
||
minify_html(src_path, dest_path)
|
||
else:
|
||
# Kopiere nicht bearbeitbare Dateien direkt in den Zielordner
|
||
shutil.copy2(src_path, dest_path)
|
||
|
||
def strip_files(src_dir, dest_dir):
|
||
# Erstelle den Zielordner und alle dazugehörigen Unterordner, falls sie nicht existieren
|
||
os.makedirs(dest_dir, exist_ok=True)
|
||
|
||
# Durchlaufe alle Dateien und Unterverzeichnisse im Quellordner
|
||
for root, _, files in os.walk(src_dir):
|
||
for filename in files:
|
||
src_path = os.path.join(root, filename)
|
||
dest_path = os.path.relpath(src_path, src_dir)
|
||
dest_path = os.path.join(dest_dir, dest_path)
|
||
|
||
# Verarbeite nur Dateien (keine Unterverzeichnisse)
|
||
process_file(src_path, dest_path)
|
||
|
||
|
||
def gzip_file(src_path, dst_path):
|
||
|
||
with open(src_path, 'rb') as src, gzip.open(dst_path, 'wb') as dst:
|
||
for chunk in iter(lambda: src.read(4096), b""):
|
||
dst.write(chunk)
|
||
|
||
|
||
def getListOfFiles(dirName):
|
||
# create a list of file and sub directories
|
||
# names in the given directory
|
||
listOfFile = os.listdir(dirName)
|
||
allFiles = list()
|
||
# Iterate over all the entries
|
||
for entry in listOfFile:
|
||
# Create full path
|
||
fullPath = os.path.join(dirName, entry)
|
||
# If entry is a directory then get the list of files in this directory
|
||
if os.path.isdir(fullPath):
|
||
allFiles = allFiles + getListOfFiles(fullPath)
|
||
else:
|
||
allFiles.append(fullPath)
|
||
|
||
return allFiles
|
||
|
||
def remove_prefix(text, prefix):
|
||
if text.startswith(prefix):
|
||
return text[len(prefix):]
|
||
return text # or whatever
|
||
|
||
# Compress files from 'data_src/' to 'data/'
|
||
|
||
|
||
def gzip_webfiles(source, target, env):
|
||
# Filetypes to compress
|
||
filetypes_to_gzip = ['.css', '.png', '.js', '.ico', '.woff2', '.json']
|
||
print('\nGZIP: Starting gzip-Process for LittleFS-Image...\n')
|
||
data_src_dir_path = os.path.join(env.get('PROJECT_DIR'), 'data_src')
|
||
data_temp_dir_path = os.path.join(env.get('PROJECT_DIR'), 'data_stripped')
|
||
strip_files(data_src_dir_path, data_temp_dir_path)
|
||
data_dir_path = env.get('PROJECT_DATA_DIR')
|
||
# check if data and datasrc exist. If the first exists and not the second, it renames it
|
||
if(os.path.exists(data_dir_path) and not os.path.exists(data_temp_dir_path)):
|
||
print('GZIP: Directory "'+data_dir_path +
|
||
'" exists, "'+data_temp_dir_path+'" is not found.')
|
||
print('GZIP: Renaming "' + data_dir_path +
|
||
'" to "' + data_temp_dir_path + '"')
|
||
os.rename(data_dir_path, data_temp_dir_path)
|
||
# Delete the 'data' directory
|
||
if(os.path.exists(data_dir_path)):
|
||
print('GZIP: Deleting the "data" directory ' + data_dir_path)
|
||
shutil.rmtree(data_dir_path)
|
||
# Recreate empty 'data' directory
|
||
print('GZIP: Re-creating an empty data directory ' + data_dir_path)
|
||
os.mkdir(data_dir_path)
|
||
# Determine the files to compress
|
||
|
||
files_to_copy = []
|
||
files_to_gzip = []
|
||
|
||
all_data_src = getListOfFiles(data_temp_dir_path)
|
||
for file in all_data_src:
|
||
file_name, file_extension = os.path.splitext(file)
|
||
print(file_name + " has filetype " + file_extension)
|
||
if file_extension in filetypes_to_gzip:
|
||
files_to_gzip.append(file)
|
||
else:
|
||
filename_subdir = remove_prefix(file, data_temp_dir_path)
|
||
files_to_copy.append(filename_subdir)
|
||
|
||
for file in files_to_copy:
|
||
print('GZIP: Copying file from: ' + data_temp_dir_path + file + ' to: ' + data_dir_path + file)
|
||
os.makedirs(os.path.dirname(data_dir_path + file), exist_ok=True)
|
||
shutil.copy(data_temp_dir_path + file, data_dir_path + file)
|
||
# Compress and move files
|
||
|
||
was_error = False
|
||
try:
|
||
for source_file_path in files_to_gzip:
|
||
print('GZIP: compressing... ' + source_file_path)
|
||
filename_subdir = remove_prefix(source_file_path, data_temp_dir_path)
|
||
target_file_path = data_dir_path + filename_subdir
|
||
os.makedirs(os.path.dirname(target_file_path), exist_ok=True)
|
||
print('GZIP: Compressed... ' + target_file_path)
|
||
gzip_target_path = target_file_path + ".gz"
|
||
gzip_file(source_file_path, gzip_target_path)
|
||
except IOError as e:
|
||
was_error = True
|
||
print('GZIP: Failed to compress file: ' + source_file_path)
|
||
# print( 'GZIP: EXCEPTION... {}'.format( e ) )
|
||
if was_error:
|
||
print('GZIP: Failure/Incomplete.\n')
|
||
else:
|
||
print('GZIP: Compressed correctly.\n')
|
||
shutil.rmtree(data_temp_dir_path)
|
||
|
||
return
|
||
|
||
def gzip_binffiles(source, target, env):
|
||
git_revision = popen('git rev-parse --short HEAD').read().strip()
|
||
custom_flash_version = env.GetProjectOption("custom_flash_version", "0.99")
|
||
|
||
# Format the target file name
|
||
target_filename = f"filesystem_{custom_flash_version}_{git_revision}.fs"
|
||
littlefsbin = target[0].get_abspath()
|
||
targetbin = os.path.join(os.path.dirname(littlefsbin), target_filename)
|
||
shutil.copyfile(littlefsbin, targetbin)
|
||
gzip_file(targetbin, os.path.join(str(targetbin) + '.gz'))
|
||
os.remove(targetbin)
|
||
return
|
||
|
||
# IMPORTANT, this needs to be added to call the routine
|
||
env.AddPreAction('$BUILD_DIR/littlefs.bin', gzip_webfiles)
|
||
env.AddPostAction('$BUILD_DIR/littlefs.bin', gzip_binffiles)
|