Rewrote archive script in Node.js.

It requires the fs-extra module, but that's considerably less to install
than Python.
This commit is contained in:
Oliver Hamlet
2015-08-27 14:37:55 +01:00
parent d10b429708
commit ca4b31dd0b
5 changed files with 173 additions and 190 deletions
+1
View File
@@ -53,3 +53,4 @@ docs/html
docs/latex
*.mo
bower_components
node_modules
+1 -1
View File
@@ -63,6 +63,6 @@ If you're adding a new translation, LOOT's source code must be updated to recogn
* In [language.h](src/backend/helpers/language.h), add a static constant for the language to the `Language` class.
* In [language.cpp](src/backend/helpers/language.cpp), define the value for the constant you added, and update `Language::Language(const std::string& locale)`, `Language::Construct(const unsigned int code)` and `Language::Codes({...})` to include lines for your language.
* Add constants for the language in [api.h](src/api/api.h) and [api.cpp](src/api/api.cpp).
* In [archive.py](scripts/archive.py), add the language folder to the inline list on line 68.
* In [archive.js](scripts/archive.js), add the language folder to the list on line 83.
* In [installer.iss](scripts/installer.iss), add an entry for your language's translation file to the `[Files]` section.
* In [LOOT Metadata Syntax.html](docs/LOOT%20Metadata%20Syntax.html), add a row for your language to the Language Codes table.
+3 -3
View File
@@ -56,11 +56,11 @@ Although LOOT uses a cross-platform build system and cross-platform libraries, i
## Packaging Releases
Installer and zip archive releases for the main LOOT application can be handled by running the `scripts/installer.iss` and `scripts/archive.py` scripts respectively. The installer script requires [Inno Setup](http://www.jrsoftware.org/isinfo.php), while the archive script requires [Python](http://www.python.org/) (2 or 3). The installer and archive files are created in the `build/` folder, relative to the repository root.
Packaging scripts are provided for creating an installer and compressed archives on Windows.
If the unofficial Korean and Simplified Chinese Inno Setup translation files are installed alongside the official translation files, then the installer script will also offer those language options. If they are not found, the installer will be built without them.
Run the `scripts/installer.iss` [Inno Setup](http://www.jrsoftware.org/isinfo.php) script to build an installer executable in the `build` folder. If the unofficial Korean and Simplified Chinese Inno Setup translation files are installed alongside the official translation files, then the installer script will also offer those language options. If they are not found, the installer will be built without them.
If you have [Git for Windows](http://git-for-windows.github.io/) installed and you're building from a clone of this repository, the archive script will give archives descriptive names using the output of `git describe --tags --long`. If you have [7-Zip](http://7-zip.org) installed, `.7z` archives will be created. Otherwise, archives will be named `LOOT Archive` and created as deflate-compressed zip files.
The archive packaging script requires [Git for Windows](http://git-for-windows.github.io/) and [7-Zip](http://7-zip.org) to be installed. It also requires the [fs-extra](https://www.npmjs.com/package/fs-extra) Node.js module, which can be installed using `npm install fs-extra`. The script can be run using `node scripts/archive.js`, and will create `.7z` archives for LOOT and its API in the `build` folder. The archives will be given filenames using the output of `git describe --tags --long`.
## Snapshot Builds
+168
View File
@@ -0,0 +1,168 @@
#!/usr/bin/env node
// Archive packaging script. Takes one argument, which is the path to the
// repository's root. Requires 7-zip and Git to be installed, and Git to be
// available on the system path.
var child_process = require('child_process');
var path = require('path');
var fs = require('fs-extra');
if (process.argc < 3) {
var root_path = process.argv[2];
} else {
var root_path = '.';
}
var temp_path = path.join(root_path, 'build', 'archive.tmp');
function vulcanize() {
return child_process.execFileSync('node', [
'scripts/vulcanize.js',
root_path
]);
}
function getGitDescription() {
return String(child_process.execFileSync('git', [
'describe',
'--tags',
'--long'
])).slice(0, -1);
}
function compress(source_path, dest_path) {
var sevenzip_path = path.join('C:\\', 'Program Files', '7-Zip', '7z.exe');
// First remove any existing archive.
fs.removeSync(dest_path);
// The last argument must have a leading dot for the subdirectory not to
// be present in the archive, but path.join removes it, so it's prefixed.
return child_process.execFileSync(sevenzip_path, [
'a',
'-r',
dest_path,
'.' + path.sep + path.join(source_path, '*')
]);
}
function createAppArchive(dest_path) {
// Ensure that the output directory is empty.
fs.emptyDirSync(temp_path);
// Copy LOOT exectuable and CEF files.
[
'LOOT.exe',
'd3dcompiler_47.dll',
'libEGL.dll',
'libGLESv2.dll',
'libcef.dll',
'wow_helper.exe',
'natives_blob.bin',
'snapshot_blob.bin',
'cef.pak',
'cef_100_percent.pak',
'cef_200_percent.pak',
'devtools_resources.pak',
'icudtl.dat'
].forEach(function(file){
fs.copySync(
path.join(root_path, 'build', 'Release', file),
path.join(temp_path, file)
);
});
// CEF locale file.
fs.mkdirsSync(path.join(temp_path, 'resources', 'l10n'));
fs.copySync(
path.join(root_path, 'build', 'Release', 'resources', 'l10n', 'en-US.pak'),
path.join(temp_path, 'resources', 'l10n', 'en-US.pak')
);
// Translation files.
[
'es', 'ru', 'fr', 'zh_CN', 'pl',
'pt_BR', 'fi', 'de', 'da', 'ko'
].forEach(function(lang){
fs.mkdirsSync(path.join(temp_path, 'resources', 'l10n', lang, 'LC_MESSAGES'));
fs.copySync(
path.join(root_path, 'resources', 'l10n', lang, 'LC_MESSAGES', 'loot.mo'),
path.join(temp_path, 'resources', 'l10n', lang, 'LC_MESSAGES', 'loot.mo')
);
});
// UI files.
fs.mkdirsSync(path.join(temp_path, 'resources', 'ui', 'css'));
fs.copySync(
path.join(root_path, 'build', 'Release', 'resources', 'ui', 'index.html'),
path.join(temp_path, 'resources', 'ui', 'index.html')
);
fs.copySync(
path.join(root_path, 'resources', 'ui', 'css', 'dark-theme.css'),
path.join(temp_path, 'resources', 'ui', 'css', 'dark-theme.css')
);
fs.copySync(
path.join(root_path, 'resources', 'ui', 'fonts'),
path.join(temp_path, 'resources', 'ui', 'fonts')
);
// Docs.
fs.mkdirsSync(path.join(temp_path, 'docs'));
[
'images',
'licenses',
'LOOT Metadata Syntax.html',
'LOOT Readme.html'
].forEach(function(item){
fs.copySync(
path.join(root_path, 'docs', item),
path.join(temp_path, 'docs', item)
);
});
// Now compress the folder to a 7-zip archive.
compress(temp_path, dest_path);
// Finally, delete the temporary folder.
fs.removeSync(temp_path);
}
function createApiArchive(dest_path) {
// Ensure that the output directory is empty.
fs.emptyDirSync(temp_path);
// API binary.
fs.copySync(
path.join(root_path, 'build', 'Release', 'loot32.dll'),
path.join(temp_path, 'loot32.dll')
);
// API header file.
fs.mkdirsSync(path.join(temp_path, 'include', 'loot'));
fs.copySync(
path.join(root_path, 'src', 'api', 'api.h'),
path.join(temp_path, 'include', 'loot', 'api.h')
);
// Docs.
fs.mkdirsSync(path.join(temp_path, 'docs'));
fs.copySync(
path.join(root_path, 'docs', 'latex', 'refman.pdf'),
path.join(temp_path, 'docs', 'readme.pdf')
);
fs.copySync(
path.join(root_path, 'docs', 'licenses'),
path.join(temp_path, 'docs', 'licenses')
);
// Now compress the folder to a 7-zip archive.
compress(temp_path, dest_path);
// Finally, delete the temporary folder.
fs.removeSync(temp_path);
}
var git_desc = getGitDescription();
vulcanize();
createAppArchive(path.join(root_path, 'build', 'LOOT ' + git_desc + '.7z'));
createApiArchive(path.join(root_path, 'build', 'LOOT API ' + git_desc + '.7z'));
-186
View File
@@ -1,186 +0,0 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Creates an archive of a LOOT release, putting it in the 'build' folder.
# LOOT
#
# A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and
# Fallout: New Vegas.
#
# Copyright (C) 2013-2015 WrinklyNinja
#
# This file is part of LOOT.
#
# LOOT is free software: you can redistribute
# it and/or modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation, either version 3 of
# the License, or (at your option) any later version.
#
# LOOT 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
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with LOOT. If not, see
# <http://www.gnu.org/licenses/>.
import sys
import os
import shutil
import zipfile
import subprocess
# There are two compression methods available:
#
# * Zip (Deflate)
# * 7-Zip (LZMA)
#
# Python 3.3+ can also do BZIP2 and LZMA zip archives, but they don't have good
# Windows OS support, so people think they should be able to open them without
# an archiving utility, find they can't, and think the archive is broken. So
# they won't be used.
#
# Look for 7-Zip in its default install location, and use it if it is found.
# Fall back to zip if not.
#
# Archives are named using the output of `git describe --tags --long`, if Git
# is found in the current PATH. Otherwise, they will simply be named
# 'LOOT Archive'.
#
# The current path throughout the script is the `src` folder, where this script
# is located.
# Find an executable's path from its name. Like Python 3.3's shutil.which, but
# also checks likely paths for hardcoded programs.
def which(cmd):
if 'which' in dir(shutil):
exe_path = shutil.which(cmd)
else:
# Check the PATH environmental variable manually.
exe_path = None
path = os.getenv('PATH')
for p in path.split(os.path.pathsep):
p = os.path.join(p, cmd)
if os.path.exists(p) and os.access(p, os.X_OK):
exe_path = p
if not exe_path:
if cmd == '7z.exe':
sevenzip_path = os.path.join('C:\\', 'Program Files', '7-Zip', '7z.exe')
if os.path.exists(sevenzip_path):
exe_path = sevenzip_path
elif cmd == 'vulcanize.cmd':
npm = os.path.join( os.getenv('APPDATA'), 'npm' )
if (os.path.exists(npm, 'vulcanize.cmd')):
exe_path = os.path.join(npm, 'vulcanize.cmd')
return exe_path
def getNameSuffix():
# Check for a Git install, and get the revision info if found.
git = which('git.exe')
if git:
args = [git, 'describe', '--tags', '--long']
output = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
if len(output[1]) == 0:
return output[0].decode('ascii')[:-1]
return ''
def buildUIFiles():
# Vulcanize the UI's files.
subprocess.call(['node', './vulcanize.js', '..']);
def createArchive(folder_path, archive_path):
sevenzip_path = os.path.join('C:\\', 'Program Files', '7-Zip', '7z.exe')
if os.path.exists(sevenzip_path):
args = [sevenzip_path, 'a', '-r', archive_path + '.7z', os.path.join(folder_path, '*')]
subprocess.call(args)
else:
zip = zipfile.ZipFile( archive_path + '.zip', 'w', zipfile.ZIP_DEFLATED )
for root, dirs, files in os.walk(folder_path):
for file in files:
zip.write(os.path.join(root, file))
zip.close()
def createAppArchive(archive_path):
temp_path = os.path.join('..', 'build', 'archive.tmp')
# Delete the temporary folder if it already exists, then create it.
if os.path.exists(temp_path):
shutil.rmtree(temp_path)
os.makedirs(temp_path)
# Now copy everything into the temporary folder.
# LOOT executable and CEF files.
shutil.copy( os.path.join('..', 'build', 'Release', 'LOOT.exe'), temp_path )
shutil.copy( os.path.join('..', 'build', 'Release', 'd3dcompiler_47.dll'), temp_path )
shutil.copy( os.path.join('..', 'build', 'Release', 'libEGL.dll'), temp_path )
shutil.copy( os.path.join('..', 'build', 'Release', 'libGLESv2.dll'), temp_path )
shutil.copy( os.path.join('..', 'build', 'Release', 'libcef.dll'), temp_path )
shutil.copy( os.path.join('..', 'build', 'Release', 'wow_helper.exe'), temp_path )
shutil.copy( os.path.join('..', 'build', 'Release', 'natives_blob.bin'), temp_path )
shutil.copy( os.path.join('..', 'build', 'Release', 'snapshot_blob.bin'), temp_path )
shutil.copy( os.path.join('..', 'build', 'Release', 'cef.pak'), temp_path )
shutil.copy( os.path.join('..', 'build', 'Release', 'cef_100_percent.pak'), temp_path )
shutil.copy( os.path.join('..', 'build', 'Release', 'cef_200_percent.pak'), temp_path )
shutil.copy( os.path.join('..', 'build', 'Release', 'devtools_resources.pak'), temp_path )
shutil.copy( os.path.join('..', 'build', 'Release', 'icudtl.dat'), temp_path )
# Translation files.
os.makedirs(os.path.join(temp_path, 'resources', 'l10n'))
shutil.copy( os.path.join('..', 'build', 'Release', 'resources', 'l10n', 'en-US.pak'), os.path.join(temp_path, 'resources', 'l10n') )
for lang in ['es', 'ru', 'fr', 'zh_CN', 'pl', 'pt_BR', 'fi', 'de', 'da', 'ko']:
os.makedirs(os.path.join(temp_path, 'resources', 'l10n', lang, 'LC_MESSAGES'))
shutil.copy( os.path.join('..', 'resources', 'l10n', lang, 'LC_MESSAGES', 'loot.mo'), os.path.join(temp_path, 'resources', 'l10n', lang, 'LC_MESSAGES') )
# UI files.
os.makedirs( os.path.join(temp_path, 'resources', 'ui', 'css') )
shutil.copy( os.path.join('..', 'build', 'Release', 'resources', 'ui', 'index.html'), os.path.join(temp_path, 'resources', 'ui') )
shutil.copy( os.path.join('..', 'resources', 'ui', 'css', 'dark-theme.css'), os.path.join(temp_path, 'resources', 'ui', 'css') )
shutil.copytree( os.path.join('..', 'resources', 'ui', 'fonts'), os.path.join(temp_path, 'resources', 'ui', 'fonts') )
# Docs.
shutil.copytree( os.path.join('..', 'docs', 'images'), os.path.join(temp_path, 'docs', 'images') )
shutil.copytree( os.path.join('..', 'docs', 'licenses'), os.path.join(temp_path, 'docs', 'licenses') )
shutil.copy( os.path.join('..', 'docs', 'LOOT Metadata Syntax.html'), os.path.join(temp_path, 'docs') )
shutil.copy( os.path.join('..', 'docs', 'LOOT Readme.html'), os.path.join(temp_path, 'docs') )
# Now compress the temporary folder.
createArchive(temp_path, archive_path);
# And finally, delete the temporary folder.
shutil.rmtree(temp_path)
def createApiArchive(archive_path):
temp_path = os.path.join('..', 'build', 'archive.tmp')
# Delete the temporary folder if it already exists, then create it.
if os.path.exists(temp_path):
shutil.rmtree(temp_path)
os.makedirs(temp_path)
# Create directories in temporary folder.
os.makedirs( os.path.join(temp_path, 'include', 'loot') )
os.makedirs( os.path.join(temp_path, 'docs') )
# Now copy everything into the temporary folder.
shutil.copy( os.path.join('..', 'build', 'Release', 'loot32.dll'), temp_path )
shutil.copy( os.path.join('..', 'src', 'api', 'api.h'), os.path.join(temp_path, 'include', 'loot') )
shutil.copy( os.path.join('..', 'docs', 'latex', 'refman.pdf'), os.path.join(temp_path, 'docs', 'readme.pdf') )
shutil.copytree( os.path.join('..', 'docs', 'licenses'), os.path.join(temp_path, 'docs', 'licenses') )
# Now compress the temporary folder.
createArchive(temp_path, archive_path);
# And finally, delete the temporary folder.
shutil.rmtree(temp_path)
# Create the archives.
archive_suffix = getNameSuffix()
buildUIFiles();
createAppArchive( os.path.join('..', 'build', 'LOOT ' + archive_suffix) )
createApiArchive( os.path.join('..', 'build', 'LOOT API ' + archive_suffix) )