From 826f74a5e95075fe5c6d0f40ac3c526c965bd63d Mon Sep 17 00:00:00 2001 From: Pavel Brychta Date: Thu, 2 Jul 2020 09:43:51 +0200 Subject: [PATCH] Prvni odevzdani --- README.md | 65 +++++++++++++- generate.py | 248 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 312 insertions(+), 1 deletion(-) create mode 100644 generate.py diff --git a/README.md b/README.md index fedd0b7..663015e 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,66 @@ # WebApp_builder -Skript pro sestavování webových aplikací pro ESP8266/ESP32. Používá se jako Git submodul u projektu. \ No newline at end of file +Skript pro sestavování webových aplikací pro ESP8266/ESP32. Používá se jako Git submodul u projektu. + +## Návod k použití + +Pro svoji práci používá soubor `project.json`, který obsahuje parametry pro sestavení. Povinná sekce je sekce `build`, která musí obsahovat následující: + + * `approotdir` - cesta ke kořenovému adresáři projektu +```json + "approotdir":"./", +``` + * `outputdir` - cesta k adresáři, do kterého je umístěna sestavená verze projektu bez použití komprese souborů (určena pro kontrolu a případné ladění na PC) +```json + "outputdir":"./dist/", +``` + * `datadir` - cesta k adresáři, do kterého je generovaný výsledný obsah včetně případné komprese souborů +```json + "datadir":"../AZP_IoT_PIO/data/", +``` + * `flist` - jméno souboru s obsahem webu pro případnou automatickou aktualizaci ze zařízení. Není-li uvedeno, seznam se negeneruje. +```json + "flist": "flist.txt", +``` + * `combine` - seznam souborů, vzniklých kombinací (spojením) souborů jiných. Určeno pro minimalizaci současně stahovaných souborů. +```json + "combine": [ + {"name": "js/app.js", + "items":[ + "js/l10n/l10n.js", + "js/drooltip/package/js/build/drooltip.js", + "js/MsgPop/js/msgPop.js", + "js/sparkline/sparkline.js", + "js/es6-promise.auto.min.js", + "js/SweetAlert2/dist/sweetalert2.min.js", + "js/menu.js", + "js/app.js" + ] + } + ], +``` + * `copy` - seznam souborů a adresářů pro vytváření finální aplikace, obashuje také příznaky pro kompresi +```json + "copy": [ + {"src":"img/","dst":""}, + {"src":"lang/","dst":""}, + {"src":"html/favicon.png","dst":"favicon.png"}, + {"src":"html/index.htm","dst":"index.htm","compress": true,"istemplate":true,"flistforce":true}, + {"src":"html/setup.htm","dst":"setup.htm","compress": true,"istemplate":true,"flistforce":true}, + {"src":"html/update.htm","dst":"update.htm","istemplate":true}, + {"src":"devices/basic.json","dst":"basic.json","flistignore": true}, + {"src":"devices/acdc.json","dst":"acdc.json","flistignore": true}, + {"src":"devices/nmcu.json","dst":"nmcu.json","flistignore": true}, + {"src":"devices/creas.json","dst":"creas.json","flistignore": true}, + {"src":"js/microajax.js","dst":"","compress": true}, + {"synthetic":true, "src":"js/app.js","dst":"","compress": true}, + {"synthetic":true, "src":"css/app.css","dst":"","compress": true}, + {"src":"html/.htaccess","dst":".htaccess","flistignore":true} + ] +``` +Doplňující parametry v sekci `copy` jsou: + * `compress` - při kopírování do `datadir` se použije komprese gzip a jméno souboru se doplní o příponu `.gz`. + * `istemplate` - při kopírování se použije textový template engine s nahrazováním - určeno pro uložení čísla verze + * `flistforce` - v souboru se seznamem se použije klíčové slovo `force`, které vynutí násilný přepis souboru v cílovém zařízení + * `flistignore` - soubor se neuloží do seznamu v souboru + * `syntehtic` - příznak, že soubor pro přesun vznikl pomocí sekce `combine` a nemá se tedy hledat ve zdrojových adresářích diff --git a/generate.py b/generate.py new file mode 100644 index 0000000..f1b5172 --- /dev/null +++ b/generate.py @@ -0,0 +1,248 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import gzip, shutil, os, errno, json, subprocess +from distutils import dir_util +from string import Template + +__author__ = "Pavel Brychta" +__copyright__ = "Copyright (c) 2019-2020, Pavel Brychta" +__credits__ = ["Pavel Brychta"] +__license__ = "Private" +__version__ = "1.1.0" +__maintainer__ = "Pavel Brychta" +__email__ = "Pablo@xPablo.cz" +__status__ = "Private Beta" + +PROJECT_FILE = "project.json" # soubor s popisem sestaveni projektu + +project = {} +templatevars = {} +flist = None +flistcontent = "" +version = "" + + +# Vycisteni ciloveho adresare (bez jeho vymazu - jen vyprazdneni) +def cleanup(path): + for root, dirs, files in os.walk(path): + for f in files: + os.unlink(os.path.join(root, f)) + for d in dirs: + shutil.rmtree(os.path.join(root, d), ignore_errors=True, onerror=None) + + +# Vytvoreni pripadne chybejici cesty +def maketree(filename): + if not os.path.exists(os.path.dirname(filename)): + try: + os.makedirs(os.path.dirname(filename)) + except OSError as exc: # ochrana proti race condition + if exc.errno != errno.EEXIST: + raise + + +# Kopirovani adresaroveho stromu na cilove misto +def copytree(src, dst): + for item in os.listdir(src): + s = os.path.normpath(os.path.join(src, item)) + d = os.path.normpath(os.path.join(dst, item)) + maketree(d) + if os.path.isdir(s): + print(s + ' (D)-> ' + d) + copytree(s, d) + else: + print(s + ' (F)-> ' + d) + shutil.copy2(s, d) + + +# Kopirovani seznamu souboru +def copyfiles(files, ffrom, where): + + for entry in files: + if "synthetic" not in entry: + inf = entry["src"] + outf = entry["dst"] + if outf == '': + outf = inf + if os.path.isdir(ffrom + inf): + copytree(ffrom + inf, where + outf) + else: + if "istemplate" in entry and entry["istemplate"]: + with open(ffrom + inf, 'r') as infile: + print(ffrom + inf + ' (T)-> ' + where + outf) + maketree(where + outf) + # https://stackoverflow.com/questions/4406102/light-weight-template-engine-for-python + form = Template(infile.read()).substitute(templatevars) + outfile = open(where + outf, 'w') + outfile.write(form) + outfile.close() + else: + with open(ffrom + inf, 'rb') as infile: + print(ffrom + inf + ' (F)-> ' + where + outf) + maketree(where + outf) + outfile = open(where + outf, 'wb') + outfile.write(infile.read()) + outfile.close() + + +# Spojovani soubru +def joinfilesindir(dir, outfile): + for item in os.listdir(dir): + fn = os.path.join(dir, item) + if os.path.isfile(fn): + with open(fn, 'rb') as infile: + outfile.write(infile.read()) + else: + if not fn.endswith("/"): + fn += "/" + joinfilesindir(fn, outfile) + + +def joinfiles(srclist, srcPath, outfName): + outfName = os.path.normpath(outfName) + maketree(outfName) + print(outfName + ' =') + with open(outfName, 'wb') as outfile: + for fname in srclist: + fname = os.path.normpath(fname) + print(' + ' + fname) + if os.path.isfile(srcPath + fname): + with open(srcPath + fname, 'rb') as infile: + outfile.write(infile.read()) + else: + joinfilesindir(srcPath + fname, outfile) + outfile.close() + + +def copycompresstree(src, dst, compress, dirbase): + global flist + global flistcontent + + for item in os.listdir(src): + s = os.path.normpath(os.path.join(src, item)) + d = os.path.normpath(os.path.join(dst, item)) + maketree(d) + if os.path.isdir(s): + d = os.path.normpath(os.path.join(dst, item)) + print(s + ' (D)-> ' + d) + copycompresstree(s, d, compress, dirbase) + else: + dopack = 0 + rpath = os.path.relpath(dst, dirbase) + if rpath == ".": + rpath = "" + else: + rpath = "/" + rpath + fignore = False + fforce = False + for entry in compress: + target = os.path.normpath(entry["src"]) + if entry["dst"] != "": + target = os.path.normpath(entry["dst"]) + if s.endswith(target): + if "compress" in entry and entry["compress"]: + dopack += 1 + if "flistignore" in entry: + fignore = True + if "flistforce" in entry: + fforce = True + if dopack != 0: + d = os.path.normpath(os.path.join(dst, item + ".gz")) + print(s + ' (C)-> ' + d) + # packing & copying + with open(s, 'rb') as infile: + maketree(d) + outfile = gzip.open(d, 'wb') + outfile.writelines(infile) + outfile.close() + if flist is not None and not fignore: + flc = item + ".gz" + if fforce: + flc += ",force" + flistcontent += rpath + "/" + flc + "\r\n" + else: + d = os.path.normpath(os.path.join(dst, item)) + print(s + ' (F)-> ' + d) + shutil.copy2(s, d) + if flist is not None and not fignore: + flc = item + if fforce: + flc += ",force" + flistcontent += rpath + "/" + flc + "\r\n" + + +# Ziskani verze repozitare z gitu +def getgitversion(): + after = b"0" + mod = b"" + try: + tag = subprocess.check_output(["git", "describe", "--tags", "--always"]).strip() + except: + tag = b"0.0.0" + if tag.find(b".") < 0: + tag = b"0.0.0" + if tag == b"0.0.0": + after = subprocess.check_output(["git", "rev-list", "--all", "--count"]).strip() + else: + if tag.find(b"-") > 0: + taginfo = tag.split(b'-') + tag = taginfo[0] + after = taginfo[1] + else: + after = subprocess.check_output(["git", "rev-list", tag + "..HEAD", "--count"]).strip() + mod = subprocess.check_output(["git", "diff-index", "HEAD"]).strip() + if after == b"0": + revision = tag + else: + revision = tag + b'.' + after + if mod != b"": + revision = revision + b"m" + return revision.decode("utf-8") + + +if __name__ == "__main__": + + version = getgitversion() + templatevars['version'] = version + print("Web version " + version) + with open(PROJECT_FILE) as fd: + project = json.load(fd) + + if "build" in project: + build = project["build"] + + approotdir = build["approotdir"] + outputdir = build["outputdir"] + datadir = build["datadir"] + if "flist" in build: + flist = build["flist"] + + # Vycistime vystupni adresare + cleanup(outputdir) + cleanup(datadir) + + # Zacneme kombinaci soubou + if "combine" in build: + combine = build["combine"] + for entry in combine: + outname = entry["name"] + items = entry["items"] + joinfiles(items, approotdir, outputdir + outname) + + # Pokracujeme kopirovanim souboru/adresaru + if "copy" in build: + copy = build["copy"] + copyfiles(copy, approotdir, outputdir) + + # .. a koncime prekopirovanim souboru do vystupniho adresare s prubeznou kompresi pozadovanych souboru a vytvareni flist.txt + deploy = [] + if flist is not None: + flistcontent = ":" + version + "\r\n" + if "copy" in build: + deploy = build["copy"] + copycompresstree(outputdir, datadir, deploy, datadir) + if flist is not None: + with open(os.path.normpath(os.path.join(datadir, flist)), 'wb') as outfile: + outfile.write(flistcontent.encode('utf-8')) + outfile.close()