oxjs/tools/build/build.py

411 lines
15 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
2013-05-10 12:59:58 +00:00
# -*- coding: utf-8 -*-
#vim: et:ts=4:sw=4:sts=4
import datetime
import json
2010-09-05 14:24:22 +00:00
import os
2011-04-22 22:03:10 +00:00
import re
2011-04-25 09:12:02 +00:00
import shutil
2012-03-30 21:12:00 +00:00
import subprocess
import sys
2012-04-05 20:42:35 +00:00
import tarfile
2012-04-05 15:27:27 +00:00
import time
import ox
2014-05-10 20:39:13 +00:00
def get_version():
if os.path.exists('../../.git'):
revision = subprocess.Popen(
['git', 'rev-list', 'HEAD', '--count'], stdout=subprocess.PIPE
2014-09-30 21:19:50 +00:00
).communicate()[0].strip().decode('utf-8')
2014-05-10 20:47:44 +00:00
revision = int(revision) - 94
2014-05-10 20:39:13 +00:00
else:
revision = 'x+' + datetime.datetime.now().strftime('%Y%m%d%H%M')
2014-05-10 20:39:13 +00:00
return '0.1.%s' % revision
2012-04-05 20:42:35 +00:00
def build_oxjs(downloads=False, geo=False):
2011-10-07 01:19:00 +00:00
base_path = os.path.dirname(__file__)
if base_path:
os.chdir(base_path)
2012-04-05 15:27:27 +00:00
root_path = '../../'
source_path = root_path + 'source/'
2015-04-18 10:31:55 +00:00
dev_path = root_path + 'dev.tmp/'
min_path = root_path + 'min.tmp/'
2012-04-05 15:27:27 +00:00
locales = {}
2014-05-10 20:39:13 +00:00
version = get_version()
2012-04-05 15:27:27 +00:00
year = time.strftime('%Y', time.gmtime())
2012-04-05 20:42:35 +00:00
comment = ' OxJS %s (c) %s 0x2620, dual-licensed GPL/MIT, see https://oxjs.org for details ' % (version, year)
2012-04-05 15:27:27 +00:00
2014-09-26 16:57:12 +00:00
# Empty dev and min
for path in [dev_path, min_path]:
if os.path.exists(path):
for item in os.listdir(path):
full_path = '%s%s' % (path, item)
if os.path.isdir(full_path):
2018-01-18 12:33:20 +00:00
if not (geo is False and item == 'Geo'):
shutil.rmtree(full_path)
else:
os.remove(full_path)
# Ox.UI Theme Data
theme_data = {}
themes = [dirname for dirname in os.listdir(source_path + 'UI/themes/') if not dirname[0] in '._']
for theme in themes:
theme_data[theme] = read_jsonc(source_path + 'UI/themes/%s/json/theme.jsonc' % theme)
theme_data[theme]['themeClass'] = 'OxTheme' + theme[0].upper() + theme[1:]
# Ox.UI CSS
2014-09-30 21:19:50 +00:00
css = read_text(source_path + 'UI/css/UI.css')
css = css.replace('$import', '\n'.join([
2014-01-11 05:02:26 +00:00
'@import url("../themes/%s/css/theme.css?%s");' % (theme, version) for theme in themes
]))
write_file('%sUI/css/UI.css' % dev_path, css)
2014-09-26 16:57:12 +00:00
write_file('%sUI/css/UI.css' % min_path, css)
# Ox.UI Theme CSS
2014-09-30 21:19:50 +00:00
css = read_text(source_path + 'UI/css/theme.css')
for theme in themes:
theme_css = parse_css(css, theme_data[theme])
theme_css = theme_css.replace('.png)', '.png?%s)' % version)
write_file('%sUI/themes/%s/css/theme.css' % (dev_path, theme), theme_css)
2014-09-26 16:57:12 +00:00
write_file('%sUI/themes/%s/css/theme.css' % (min_path, theme), theme_css)
# Ox.UI SVGs
ui_images = {}
path = source_path + 'UI/svg/'
for filename in [filename for filename in os.listdir(path) if not filename[0] in '._']:
2014-09-30 21:19:50 +00:00
svg = read_text(path + filename)
svg = re.sub('\n\s*', '', svg)
svg = re.sub('<!--.+?-->', '', svg)
2013-05-08 14:48:07 +00:00
# temporary fix for Chrome SVG bug, remove later!
svg = re.sub('width="256" height="256"', 'width="10" height="10" viewBox="0 0 255 255"', svg)
# end fix
ui_images[filename[:-4]] = svg
if filename.startswith('symbolLoading'):
for theme in themes:
theme_svg = re.sub('#808080', format_hex(theme_data[theme]['symbolDefaultColor']), svg)
write_file('%sUI/themes/%s/svg/%s' % (dev_path, theme, filename), theme_svg)
2014-09-26 16:57:12 +00:00
write_file('%sUI/themes/%s/svg/%s' % (min_path, theme, filename), theme_svg)
# copy & link
2014-09-26 16:57:12 +00:00
ui_files = {'dev': [], 'min': []}
for path, dirnames, filenames in os.walk(source_path):
for filename in filenames:
2018-01-18 12:33:20 +00:00
if '_' not in path and filename[0] not in '._' \
2011-12-30 10:06:45 +00:00
and not filename.endswith('~') \
and not filename.endswith('.css') \
2018-01-18 12:33:20 +00:00
and '/UI/svg' not in path \
and (geo or '/Geo/' not in path):
2014-09-26 16:57:12 +00:00
# write copies in min path
source = os.path.join(path, filename)
is_jquery = re.search('^jquery-[\d\.]+\.js$', filename)
is_jquery_min = re.search('^jquery-[\d\.]+\.min\.js$', filename)
is_jquery_plugin = re.search('^jquery\..*?\.js$', filename)
is_jsonc = re.search('\.jsonc$', filename)
if is_jquery or is_jquery_min:
2014-09-26 16:57:12 +00:00
target = os.path.join(path.replace(source_path, min_path), 'jquery.js')
else:
2014-09-26 16:57:12 +00:00
target = os.path.join(path.replace(source_path, min_path), filename)
if is_jquery_plugin:
2014-09-26 16:57:12 +00:00
ui_files['dev'].append(target.replace(min_path, ''))
ui_files['min'].append(target.replace(min_path, ''))
2018-01-18 12:33:20 +00:00
if '/Ox/js/' not in source and '/UI/js/' not in source and not is_jquery:
if re.match('^Ox\..+\.js$', filename) or is_jsonc:
2014-09-30 21:19:50 +00:00
js = read_text(source)
print('minifiy and write', filename, target)
write_file(target, ox.js.minify(js, '' if is_jsonc else comment))
else:
copy_file(source, target)
# write links in dev path
parts = os.path.join(path.replace(source_path, ''), filename).split('/')
for i, part in enumerate(parts):
if i < len(parts) - 1:
parts[i] = '..'
link_source = '/'.join(parts).replace(filename, os.path.join(path, filename))[3:]
2014-09-26 16:57:12 +00:00
link_target = target.replace(min_path, dev_path)
if not is_jquery_min:
write_link(link_source, link_target)
# locales
2014-09-26 16:57:12 +00:00
match = re.search('/(\w+)/json/locale.(\w+).json', source)
if match:
module = match.group(1)
locale = match.group(2)
2018-01-18 12:33:20 +00:00
if module not in locales:
locales[module] = []
locales[module].append(locale)
# remove dangling links from dev tree that might
# be left over from renamed or removed files
for path, dirnames, filenames in os.walk(dev_path):
for f in filenames:
f = os.path.join(path, f)
if os.path.islink(f) and not os.path.exists(f):
os.unlink(f)
# Ox.js
filenames = [
2012-05-25 09:41:31 +00:00
[
2018-01-18 12:33:20 +00:00
'Core.js' # has to run first so that Ox is defined
2012-05-25 09:41:31 +00:00
],
[
2018-01-18 12:33:20 +00:00
'Function.js', # getSortValue (Array.js) depends on Ox.cache
'Polyfill.js' # FIXME: not clear if needed here
2012-09-03 22:45:43 +00:00
],
[
2018-01-18 12:33:20 +00:00
'Array.js', # Ox.slice (Collection.js) depends on Ox.toArray, salt (HTML.js) depends on Ox.range
'String.js', # salt (HTML.js) depends on Ox.char
'Type.js' # Ox.typeOf needed in Collection.js FF3.6 for Ox.slice fallback
2012-09-03 22:45:43 +00:00
],
[
2018-01-18 12:33:20 +00:00
'Collection.js', # Ox.PATH (Constants.js) depends on Ox.slice
'Math.js' # Ox.MAX_LATITUDE (Constants.js) depends on Ox.sinh
2012-05-25 09:41:31 +00:00
]
]
js = ''
js_dir = 'Ox/js/'
ox_files = []
for group in filenames:
ox_files.append([])
for filename in group:
ox_files[-1].append(js_dir + filename)
ox_files.append([])
2018-01-18 12:33:20 +00:00
filenames = sum(filenames, []) # flatten
2012-05-25 16:46:30 +00:00
for filename in sorted(os.listdir(source_path + js_dir)):
2018-01-18 12:33:20 +00:00
if filename not in filenames \
2011-12-30 15:38:50 +00:00
and not filename.startswith('.') \
and not filename.startswith('_') \
2011-12-30 15:38:50 +00:00
and not filename.endswith('~'):
filenames.append(filename)
for filename in filenames:
2014-09-30 21:19:50 +00:00
js += read_text(source_path + js_dir + filename) + '\n'
if not js_dir + filename in sum(ox_files, []):
ox_files[-1].append(js_dir + filename)
js = re.sub(
'Ox.LOCALES = \{\}',
'Ox.LOCALES = ' + json.dumps(locales, indent=4, sort_keys=True),
js
)
js = re.sub(
"Ox.VERSION = '([\d\.]+)'",
"Ox.VERSION = '%s'" % version,
js
)
write_file(dev_path + '/Ox/json/' + 'Ox.json', json.dumps({
'files': ox_files,
'locales': locales,
'version': version
}, indent=4, sort_keys=True))
2014-09-26 16:57:12 +00:00
write_file(min_path + 'Ox.js', ox.js.minify(js, comment))
# Ox.UI
js = ''
root = source_path + 'UI/'
for path, dirnames, filenames in os.walk(root):
for filename in sorted(filenames):
# jquery gets included by Ox.UI loader
# locale json files are loaded lazily
# Ox.UI.css imports all other css files
# svgs are loaded as URLs or dataURLs
2013-07-29 23:25:40 +00:00
# Ox.UI PNGs are loaded on demand
2018-01-18 12:33:20 +00:00
if path != root and '_' not in path and filename[0] not in '._' \
and not filename.endswith('~') \
2018-01-18 12:33:20 +00:00
and 'jquery' not in filename \
and 'locale' not in filename \
and not filename.endswith('theme.css') \
and not filename.endswith('.svg') \
2018-01-18 12:33:20 +00:00
and 'UI/png' not in path:
ui_files['dev'].append(os.path.join(path.replace(source_path, ''), filename))
2018-01-18 12:33:20 +00:00
if '/js/' not in path:
2014-09-26 16:57:12 +00:00
ui_files['min'].append(os.path.join(path.replace(source_path, ''), filename))
if filename.endswith('.js'):
2014-09-30 21:19:50 +00:00
js += read_text(os.path.join(path, filename)) + '\n'
2014-09-26 16:57:12 +00:00
filename = min_path + 'UI/js/UI.js'
write_file(filename, ox.js.minify(js, comment))
2014-09-30 21:19:50 +00:00
2014-09-26 16:57:12 +00:00
ui_files['min'].append(filename.replace(min_path, ''))
write_file(min_path + 'UI/json/UI.json', json.dumps({
'files': sorted(ui_files['min']),
'images': ui_images
}, sort_keys=True))
write_file(dev_path + 'UI/json/UI.json', json.dumps({
'files': sorted(ui_files['dev']),
'images': ui_images
}, indent=4, sort_keys=True))
ui_files['dev'].append('UI/UI.js')
2012-04-05 15:27:27 +00:00
# index
data = {
# sum(list, []) is flatten
2014-09-30 21:19:50 +00:00
'documentation': sorted(sum(ox_files, [])) + sorted(list(filter(
2012-04-05 20:42:35 +00:00
lambda x: re.search('\.js$', x),
2012-04-05 15:27:27 +00:00
ui_files['dev']
2014-09-30 21:19:50 +00:00
)) + ['%s/%s.js' % (x, x) for x in ['Geo', 'Image', 'Unicode']]),
2012-06-21 21:15:35 +00:00
'examples': sorted(sum(map(
2014-09-30 21:19:50 +00:00
lambda x: list(filter(
2012-06-21 21:15:35 +00:00
lambda x: not re.search('/[._]', x),
map(
lambda y: x + '/' + y,
os.listdir(root_path + 'examples/' + x)
)
2014-09-30 21:19:50 +00:00
)),
list(filter(
2012-06-21 21:15:35 +00:00
lambda x: not re.search('^[._]', x),
os.listdir(root_path + 'examples/')
2014-09-30 21:19:50 +00:00
))
), [])) if os.path.exists(root_path + 'examples/') else (),
2014-09-30 21:19:50 +00:00
'readme': list(map(
2012-04-05 15:56:53 +00:00
lambda x: {
'date': time.strftime(
'%Y-%m-%dT%H:%M:%SZ',
time.gmtime(os.path.getmtime(root_path + 'readme/' + x))
2012-04-05 15:56:53 +00:00
),
'id': x.split('.')[0],
'title': get_title(root_path + 'readme/' + x)
2012-04-05 15:56:53 +00:00
},
2012-04-05 15:27:27 +00:00
filter(
lambda x: not re.search('^[._]', x) and re.search('\.html$', x),
os.listdir(root_path + 'readme/')
2012-04-05 15:27:27 +00:00
)
2014-09-30 21:19:50 +00:00
))
2012-04-05 15:27:27 +00:00
}
write_file(root_path + 'index.json', json.dumps(data, indent=4, sort_keys=True))
2012-04-05 20:42:35 +00:00
# downloads
if downloads:
data = {
2012-06-18 10:19:05 +00:00
'date': time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()),
2014-09-26 16:57:12 +00:00
'size': {'oxjs': os.path.getsize(min_path + 'Ox.js')},
'version': version
}
2012-04-05 20:42:35 +00:00
download_path = root_path + 'downloads/'
2012-06-13 13:52:23 +00:00
# source
source_file = download_path + 'OxJS.%s.source.tar.gz' % version
data['size']['source'] = write_tarfile(source_file, root_path, 'OxJS', filter_source)
2012-06-17 11:24:41 +00:00
write_link(source_file.replace(download_path, ''), source_file.replace(version, 'latest'))
2014-09-26 16:57:12 +00:00
# min
min_file = download_path + 'OxJS.%s.min.tar.gz' % version
data['size']['min'] = write_tarfile(min_file, root_path, 'OxJS', filter_min)
write_link(min_file.replace(download_path, ''), min_file.replace(version, 'latest'))
# json
write_file(download_path + 'downloads.json', json.dumps(data, indent=4, sort_keys=True))
2012-04-05 20:42:35 +00:00
2014-09-26 16:57:12 +00:00
# legacy
build_path = root_path + 'build/'
if os.path.exists(build_path) and not os.path.islink(build_path[:-1]):
shutil.rmtree(build_path)
2015-04-18 10:31:55 +00:00
write_link('min', build_path[:-1])
real_dev_path = root_path + 'dev/'
real_min_path = root_path + 'min/'
if os.path.exists(real_dev_path):
shutil.rmtree(real_dev_path)
2018-01-18 12:33:20 +00:00
shutil.move(dev_path, real_dev_path)
2015-04-18 10:31:55 +00:00
if os.path.exists(real_min_path):
shutil.rmtree(real_min_path)
2018-01-18 12:33:20 +00:00
shutil.move(min_path, real_min_path)
2014-09-26 16:57:12 +00:00
2011-04-25 09:12:02 +00:00
def copy_file(source, target):
2014-09-30 20:57:36 +00:00
print('copying', source, 'to', target)
2011-04-25 09:12:02 +00:00
write_file(target, read_file(source))
2014-09-26 16:57:12 +00:00
def filter_min(tarinfo):
name = tarinfo.name
2014-09-26 16:57:12 +00:00
if name == 'OxJS' or re.search('^OxJS/min', name):
2012-04-05 20:42:35 +00:00
return tarinfo
return None
def filter_source(tarinfo):
name = tarinfo.name
if re.search('^[._]', name) or re.search('/[._]', name) or re.search('~$', name):
2012-04-05 20:42:35 +00:00
return None
2012-06-18 10:10:33 +00:00
if re.search('^OxJS/downloads', name):
return None
if name == 'OxJS/tools/geo/png/icons.png':
return None
if re.search('^OxJS/tools/geo/png/icons/', name) and (
not re.search('4096', name) or not os.path.exists(
name.replace('OxJS/', '../../').replace('icons/4096', 'flags')
)
):
2012-04-05 20:42:35 +00:00
return None
return tarinfo
def format_hex(rgb):
return '#%s' % ''.join([hex(c)[-2:].replace('x', '0').upper() for c in rgb])
def get_title(file):
2014-09-30 21:19:50 +00:00
match = re.search('<h1>(.+)</h1>', read_text(file))
return match.groups()[0] if match else 'Untitled'
def parse_css(css, values):
def sub(match):
key = match.group(1)
index = match.group(2)
2018-01-18 12:33:20 +00:00
value = values[key] if index is None else values[key][int(index[1:-1])]
if isinstance(value, str):
string = value
else:
if isinstance(value[0], int):
value = [value]
string = ', '.join(
['rgb%s(%s)' % (
'a' if len(vals) == 4 else '',
', '.join([str(val) for val in vals])
) for vals in value]
)
return string
return re.sub('\$(\w+)(\[\d+\])?', sub, css)
def read_file(file):
2014-09-30 20:57:36 +00:00
print('reading', file)
2014-09-30 21:19:50 +00:00
f = open(file, 'rb')
data = f.read()
f.close()
return data
2014-09-30 21:19:50 +00:00
def read_text(file):
return read_file(file).decode('utf-8')
def read_jsonc(file):
2014-09-30 21:19:50 +00:00
return ox.jsonc.loads(read_text(file))
def write_file(file, data):
2014-09-30 20:57:36 +00:00
print('writing', file)
write_path(file)
2014-09-30 21:19:50 +00:00
if not isinstance(data, bytes):
data = data.encode('utf-8')
f = open(file, 'wb')
f.write(data)
f.close()
return len(data)
2011-04-22 22:03:10 +00:00
def write_link(source, target):
2014-09-30 20:57:36 +00:00
print('linking', source, 'to', target)
2011-04-25 09:12:02 +00:00
write_path(target)
# remove files, symlinks *and broken symlinks*
if os.path.exists(target) or os.path.lexists(target):
2014-09-26 16:57:12 +00:00
if os.path.isdir(target) and not os.path.islink(target):
os.rmdir(target)
else:
os.unlink(target)
2011-04-22 22:03:10 +00:00
os.symlink(source, target)
def write_path(file):
path = os.path.split(file)[0]
if path and not os.path.exists(path):
os.makedirs(path)
def write_tarfile(file, path, arcname, filter):
2014-09-30 20:57:36 +00:00
print('writing', file)
2014-09-26 16:57:12 +00:00
f = tarfile.open(file, 'w:gz')
f.add(path, arcname=arcname, filter=filter)
f.close()
return os.path.getsize(file)
2010-09-05 14:24:22 +00:00
2018-01-18 12:33:20 +00:00
if __name__ == '__main__':
2018-01-18 12:33:20 +00:00
build_oxjs(downloads='-downloads' in sys.argv, geo='-nogeo' not in sys.argv)