diff options
Diffstat (limited to 'compute_scripts.awk')
-rw-r--r-- | compute_scripts.awk | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/compute_scripts.awk b/compute_scripts.awk new file mode 100644 index 0000000..1f3b11e --- /dev/null +++ b/compute_scripts.awk @@ -0,0 +1,205 @@ +# SPDX-License-Identifier: CC0-1.0 +# +# Process javascript files and resolve dependencies between them +# +# This file is part of Haketilo +# +# Copyright (C) 2021, Wojtek Kosior +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the CC0 1.0 Universal License as published by +# the Creative Commons Corporation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# CC0 1.0 Universal License for more details. + +function read_file(filename, + imports_state, exports_state, line, record, result) { + imports_state = "not_started" + exports_state = "not_started" + + do { + result = (getline line < filename) + if (result < 0) { + printf "error reading %s", filename + exit 1 + } + + if (imports_state == "started" && + line ~ /^([[:space:]]*\*[[:space:]]+)?IMPORT[[:space:]]+[_a-zA-Z][_a-zA-Z0-9]*[[:space:]]*$/) { + record = line + + sub(/^([[:space:]]*\*[[:space:]]+)?IMPORT[[:space:]]+/, "", record) + sub(/([[:space:]]+$)/, "", record) + + imports[filename,++import_counts[filename]] = record + } + if (imports_state == "started" && + line ~ /^([[:space:]]*\*[[:space:]]+)?IMPORTS_END[[:space:]]*$/) + imports_state = "finished" + if (imports_state == "not_started" && + line ~ /^([[:space:]]*\*[[:space:]]+)?IMPORTS_START[[:space:]]*$/) + imports_state = "started" + + if (exports_state == "started" && + line ~ /^([[:space:]]*\*[[:space:]]+)?EXPORT[[:space:]]+[_a-zA-Z][_a-zA-Z0-9]*[[:space:]]*$/) { + record = line + + sub(/^([[:space:]]*\*[[:space:]]+)?EXPORT[[:space:]]+/, "", record) + sub(/([[:space:]]+$)/, "", record) + + if (record in exports) { + printf "ERROR: '%s' exported by both %s and %s\n", + exports[record], filename > "/dev/stderr" + } + + provides[record] = filename + exports[filename,++export_counts[filename]] = record + } + if (exports_state == "started" && + line ~ /^([[:space:]]*\*[[:space:]]+)?EXPORTS_END[[:space:]]*$/) + exports_state = "finished" + if (exports_state == "not_started" && + line ~ /^([[:space:]]*\*[[:space:]]+)?EXPORTS_START[[:space:]]*$/) + exports_state = "started" + } while (result > 0) + + if (imports_state == "started") { + printf "ERROR: Unclosed IMPORTS list in '%s'\n", filename \ + > "/dev/stderr" + exit 1 + } + + if (exports_state == "started") { + printf "ERROR: Unclosed EXPORTS list in '%s'\n", filename \ + > "/dev/stderr" + exit 1 + } + + close(filename) +} + +function print_file(filename, line) { + while ((getline line < filename) > 0) + print(line) + + close(filename) +} + +function print_imports_code(filename, i, count, import_name) { + count = import_counts[filename] + for (i = 1; i <= count; i++) { + import_name = imports[filename,i] + printf "const %s = window.haketilo_exports.%s;\n", + import_name, import_name + } +} + +function print_exports_code(filename, i, count, export_name) { + count = export_counts[filename] + for (i = 1; i <= count; i++) { + export_name = exports[filename,i] + printf "window.haketilo_exports.%s = %s;\n", export_name, export_name + } +} + +function partially_wrap_file(filename) { + print_imports_code(filename) + printf "\n\n" + + print_file(filename) + + printf "\n\n" + print_exports_code(filename) +} + +function wrap_file(filename) { + print "\"use strict\";\n\n({fun: (function() {\n" + + partially_wrap_file(filename) + + print "\n})}).fun();" +} + +function compute_dependencies(filename, i, count, import_name, next_file) { + if (processed[filename] == "used") + return 0 + + if (processed[filename] == "on_stack") { + printf "import loop on %s\n", filename > "/dev/stderr" + return 1 + } + + processed[filename] = "on_stack" + + count = import_counts[filename] + for (i = 1; i <= count; i++) { + import_name = imports[filename,i] + if (!(import_name in provides)) { + printf "nothing exports %s, required by %s\n", + import_name, filename > "/dev/stderr" + return 1 + } + + if (compute_dependencies(provides[import_name]) > 0) { + printf "when satisfying %s for %s\n", + import_name, filename > "/dev/stderr" + return 1 + } + } + + processed[filename] = "used" + print filename + + return 0 +} + +function print_usage() { + printf "usage: %2 compute_scripts.awk script_dependencies|wrapped_code|partially_wrapped_code FILENAME[...]\n", + ARGV[0] > "/dev/stderr" + exit 1 +} + +function mock_exports_init() { + provides["browser"] = "exports_init.js" + provides["is_chrome"] = "exports_init.js" + provides["is_mozilla"] = "exports_init.js" + + processed["exports_init.js"] = "used" +} + +BEGIN { + operation = ARGV[1] + + if (ARGC < 3) + print_usage() + + root_filename = ARGV[2] + + for (i = 2; i < ARGC; i++) + filenames[ARGV[i]] + + mock_exports_init() + + for (filename in filenames) { + # A filename is allowed to appear multiple times in the list. + # Let's only process it once. + if (!(filename in processed)) + read_file(filename) + processed[filename] = "not_used" + } + + if (operation == "script_dependencies") { + print("exports_init.js") + if (compute_dependencies(root_filename) > 0) + exit 1 + } else if (operation == "partially_wrapped_code") { + partially_wrap_file(root_filename) + } else if (operation == "wrapped_code") { + wrap_file(root_filename) + } else { + print_usage() + } +} |