Skip to content

Commit

Permalink
build(android): include common bundle in snapshot
Browse files Browse the repository at this point in the history
chore(android): log launch time

fix(android,ios): generate platform specific bundles

fix(android): amend launch time as debug statistic

fix(android): rename snapshot method

build: refactor babel transpilation options

fix(build): bump build script ecma version

fix(build): address lint warnings

fix(android): use SystemClock.uptimeMillis()

fix(android): validate snapshot header size

fix(android): remove unused import

fix(ios): use minIosVersion
  • Loading branch information
Gary Mathews committed Aug 14, 2019
1 parent 978d625 commit 47a7ad4
Show file tree
Hide file tree
Showing 12 changed files with 179 additions and 103 deletions.
2 changes: 1 addition & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
{
"files": [ "build/**/*.js" ],
"parserOptions": {
"ecmaVersion": 2017,
"ecmaVersion": 2018,
"sourceType": "script"
}
},
Expand Down
25 changes: 17 additions & 8 deletions android/cli/commands/_build.js
Original file line number Diff line number Diff line change
Expand Up @@ -2592,14 +2592,23 @@ AndroidBuilder.prototype.copyResources = function copyResources(next) {
const tasks = [
// First copy all of the Titanium SDK's core JS files shared by all platforms.
function (cb) {
const src = path.join(this.titaniumSdkPath, 'common', 'Resources');
warnDupeDrawableFolders.call(this, src);
_t.logger.debug(__('Copying %s', src.cyan));
copyDir.call(this, {
src: src,
dest: this.buildBinAssetsResourcesDir,
ignoreRootDirs: ti.allPlatformNames
}, cb);
// Check if a snapshot has been generated.
fs.stat(path.join(this.platformPath, 'native', 'include', 'V8Snapshots.h'), (error, stat) => {
// 'V8Snapshot.h' will always exists, check size to determin if a snapshot was generated.
if (error || stat.size <= 64) {
const src = path.join(this.titaniumSdkPath, 'common', 'Resources', 'android');
warnDupeDrawableFolders.call(this, src);
_t.logger.debug(__('Copying %s', src.cyan));
copyDir.call(this, {
src: src,
dest: this.buildBinAssetsResourcesDir,
ignoreRootDirs: ti.allPlatformNames
}, cb);
return;
}
// Do not copy 'common' bundle over, as it is included in our snapshot.
return cb();
});
},

// Next, copy all files in the project's Resources directory,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ public static void updateActivityTransitionState(boolean state)
}
}

public static long START_TIME_MS = 0;

public TiApplication()
{
Log.checkpoint(TAG, "checkpoint, app created.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.SystemClock;

/**
* Titanium launch activities have a single TiContext and launch an associated
Expand Down Expand Up @@ -96,7 +97,14 @@ protected void loadScript()
{
try {
String fullUrl = resolveUrl(this.url);
KrollRuntime.getInstance().runModule(KrollAssetHelper.readAsset(fullUrl), fullUrl, activityProxy);
if (KrollAssetHelper.assetExists(fullUrl)) {
KrollRuntime.getInstance().runModule(KrollAssetHelper.readAsset(fullUrl), fullUrl, activityProxy);

// launch script does not exist, must be using snapshot
// execute startup method baked in snapshot
} else {
KrollRuntime.getInstance().runModule("global.startSnapshot(global)", fullUrl, activityProxy);
}
} finally {
Log.d(TAG, "Signal JS loaded", Log.DEBUG_MODE);
}
Expand All @@ -105,6 +113,9 @@ protected void loadScript()
@Override
protected void onCreate(Bundle savedInstanceState)
{
// set start time
TiApplication.START_TIME_MS = SystemClock.uptimeMillis();

TiApplication tiApp = getTiApp();

// If this is a TiJSActivity derived class created via "tiapp.xml" <activity/> tags,
Expand Down Expand Up @@ -181,6 +192,7 @@ protected void onResume()
if (!hasLoadedScript) {
hasLoadedScript = true;
loadScript();
Log.d(TAG, "Launched in " + (SystemClock.uptimeMillis() - TiApplication.START_TIME_MS) + " ms");
}
super.onResume();
}
Expand Down
15 changes: 15 additions & 0 deletions build/lib/android/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ const copyFiles = utils.copyFiles;
const copyAndModifyFile = utils.copyAndModifyFile;
const globCopy = utils.globCopy;

const ROOT_DIR = path.join(__dirname, '..', '..', '..');

const ANDROID_BUILD_XML = path.join(__dirname, '../../../android/build.xml');
const V8_STRING_VERSION_REGEXP = /(\d+)\.(\d+)\.\d+\.\d+/;

class Android {
/**
Expand Down Expand Up @@ -41,6 +44,18 @@ class Android {
};
}

babelOptions() {
const v8Version = require(path.join(ROOT_DIR, 'android', 'package.json')).v8.version; // eslint-disable-line security/detect-non-literal-require
const v8VersionGroup = v8Version.match(V8_STRING_VERSION_REGEXP);
const version = parseInt(v8VersionGroup[1] + v8VersionGroup[2]);

return {
targets: {
chrome: version
}
};
}

async clean() {
return ant.build(ANDROID_BUILD_XML, [ 'clean' ], this.antProperties);
}
Expand Down
22 changes: 19 additions & 3 deletions build/lib/android/snapshot.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ const path = require('path');
const ejs = require('ejs');

const ROOT_DIR = path.join(__dirname, '..', '..', '..');
const DIST_DIR = path.join(ROOT_DIR, 'dist');
const TMP_DIR = path.join(DIST_DIR, 'tmp');
const ANDROID_DIR = path.join(ROOT_DIR, 'android');
const ANDROID_PROPS = require(path.join(ANDROID_DIR, 'package.json')); // eslint-disable-line security/detect-non-literal-require
const V8_PROPS = ANDROID_PROPS.v8;
Expand All @@ -34,16 +36,30 @@ async function generateBlob(target) {
const V8_LIB_TARGET_DIR = path.resolve(V8_LIB_DIR, target);
const MKSNAPSHOT_PATH = path.join(V8_LIB_TARGET_DIR, 'mksnapshot');
const BLOB_PATH = path.join(V8_LIB_TARGET_DIR, 'blob.bin');
const STARTUP_PATH = path.join(TMP_DIR, 'startup.js');
const TI_MAIN_PATH = path.join(ROOT_DIR, 'common', 'Resources', 'ti.main.js');
const TI_MAIN_PLATFORM_PATH = path.join(TMP_DIR, 'common', 'android', 'ti.main.js');
const args = [
'--startup_blob=' + BLOB_PATH
'--startup_blob=' + BLOB_PATH,
STARTUP_PATH,
'--print-all-exceptions'
];

// Snapshot already exists, skip...
if (await fs.exists(BLOB_PATH)) {
console.warn(`Snapshot blob for ${target} already exists, skipping...`);
return;
const { blobStat, commonStat } = await Promise.all([ fs.stat(BLOB_PATH), fs.stat(TI_MAIN_PATH) ]);
if (commonStat.mtime < blobStat.mtime) {
return;
}
}

// Load platform optimized 'common' bundle
const commonBundle = await fs.readFile(TI_MAIN_PLATFORM_PATH);

// Generate 'startup.js'
const output = await promisify(ejs.renderFile)(path.join(__dirname, 'startup.ejs'), { script: commonBundle }, {});
await fs.writeFile(STARTUP_PATH, output);

// Set correct permissions for 'mksnapshot'
await fs.chmod(MKSNAPSHOT_PATH, 0o755);

Expand Down
4 changes: 4 additions & 0 deletions build/lib/android/startup.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
this.startSnapshot = function (global) {
delete global.startSnapshot;
<%- script %>
}
64 changes: 63 additions & 1 deletion build/lib/builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,19 @@
const os = require('os');
const path = require('path');
const fs = require('fs-extra');
const rollup = require('rollup').rollup;
const babel = require('rollup-plugin-babel');
const resolve = require('rollup-plugin-node-resolve');
const commonjs = require('rollup-plugin-commonjs');

const git = require('./git');
const utils = require('./utils');
const copyPackageAndDependencies = utils.copyPackageAndDependencies;
const Packager = require('./packager');

const ROOT_DIR = path.join(__dirname, '../..');
const ROOT_DIR = path.join(__dirname, '..', '..');
const DIST_DIR = path.join(ROOT_DIR, 'dist');
const TMP_DIR = path.join(DIST_DIR, 'tmp');

// platforms/OS mappings
const ALL_OSES = [ 'win32', 'linux', 'osx' ];
Expand All @@ -28,6 +34,21 @@ function thisOS() {
return osName;
}

function determineBabelOptions(babelOptions) {
const options = {
...babelOptions,
useBuiltIns: 'entry',
// DO NOT include web polyfills!
exclude: [ 'web.dom.iterable', 'web.immediate', 'web.timers' ],
corejs: 2
};

return {
presets: [ [ '@babel/env', options ] ],
exclude: 'node_modules/**'
};
}

class Builder {
constructor(program) {
this.hostOS = thisOS();
Expand Down Expand Up @@ -82,13 +103,54 @@ class Builder {
this.program.gitHash = hash || 'n/a';
}

async transpile(platform, babelOptions) {
// Copy over common dir, @babel/polyfill, etc into some temp dir
// Then run rollup/babel on it, then just copy the resulting bundle to our real destination!
// The temporary location we'll assembled the transpiled bundle
const TMP_COMMON_DIR = path.join(TMP_DIR, '_common');
const TMP_COMMON_PLAFORM_DIR = path.join(TMP_DIR, '_common', platform);

console.log(`Creating temporary 'common' directory...`); // eslint-disable-line quotes
await fs.copy(path.join(ROOT_DIR, 'common'), TMP_COMMON_PLAFORM_DIR);

// copy over polyfill and its dependencies
console.log('Copying polyfills...');
const modulesDir = path.join(TMP_COMMON_PLAFORM_DIR, 'Resources/node_modules');
// make sure our 'node_modules' directory exists
await fs.ensureDir(modulesDir);
copyPackageAndDependencies('@babel/polyfill', modulesDir);

// create a bundle
console.log('Transpile and run rollup...');
const bundle = await rollup({
input: `${TMP_COMMON_PLAFORM_DIR}/Resources/ti.main.js`,
plugins: [
resolve(),
commonjs(),
babel(determineBabelOptions(babelOptions))
],
external: [ './app', 'com.appcelerator.aca' ]
});

console.log(`Writing 'common' bundle...`); // eslint-disable-line quotes
await bundle.write({ format: 'cjs', file: path.join(TMP_DIR, 'common', platform, 'ti.main.js') });

// We used to have to copy over ti.internal, but it is now bundled into ti.main.js
// if we ever have files there that cannot be bundled or are not hooked up properly, we'll need to copy them here manually.

console.log(`Removing temporary 'common' directory...`); // eslint-disable-line quotes
await fs.remove(TMP_COMMON_DIR);
}

async build() {
await this.ensureGitHash();
console.log('Building MobileSDK version %s, githash %s', this.program.sdkVersion, this.program.gitHash);

// TODO: build platforms in parallel
for (const item of this.platforms) {
const Platform = require(`./${item}`); // eslint-disable-line security/detect-non-literal-require
const platform = new Platform(this.program);
await this.transpile(item, platform.babelOptions());
await platform.build();
}
}
Expand Down
13 changes: 12 additions & 1 deletion build/lib/ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,17 @@ class IOS {
this.timestamp = options.timestamp;
}

babelOptions() {
// eslint-disable-next-line security/detect-non-literal-require
const { minIosVersion } = require(path.join(ROOT_DIR, 'iphone/package.json'));

return {
targets: {
ios: minIosVersion
}
};
}

async clean() {
return fs.remove(path.join(IOS_ROOT, 'TitaniumKit/build'));
}
Expand Down Expand Up @@ -140,7 +151,7 @@ class IOS {
// TODO: Use copyAndModifyFile?
const contents = await fs.readFile(path.join(ROOT_DIR, 'support/iphone/main.m'), 'utf8');
const newContents = contents.replace(/(__.+?__)/g, function (match, key) {
const s = subs.hasOwnProperty(key) ? subs[key] : key;
const s = subs.hasOwnProperty(key) ? subs[key] : key; // eslint-disable-line no-prototype-builtins
return typeof s === 'string' ? s.replace(/"/g, '\\"').replace(/\n/g, '\\n') : s;
});
return fs.writeFile(dest, newContents);
Expand Down

0 comments on commit 47a7ad4

Please sign in to comment.