Skip to content
This repository was archived by the owner on Aug 7, 2021. It is now read-only.

feat: support useLibs though env.compileSnapshot and calculate the NDK path internally #1063

Merged
merged 3 commits into from
Oct 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions androidProjectHelpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,16 @@ const getMksnapshotParams = (projectDir) => {
}
};

const getRuntimeNdkRevision = (projectDir) => {
try {
const androidSettingsJSON = getAndroidSettingsJson(projectDir);
const result = androidSettingsJSON && androidSettingsJSON.ndkRevision;
return result;
} catch (e) {
return null;
}
};

const getAndroidSettingsJson = projectDir => {
const androidSettingsJsonPath = resolve(projectDir, PLATFORMS_ANDROID, "settings.json");
if (existsSync(androidSettingsJsonPath)) {
Expand All @@ -62,5 +72,6 @@ module.exports = {
ANDROID_CONFIGURATIONS_PATH,
getAndroidRuntimeVersion,
getAndroidV8Version,
getMksnapshotParams
};
getMksnapshotParams,
getRuntimeNdkRevision
};
9 changes: 8 additions & 1 deletion lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,15 @@ function isWinOS() {
return os.type() === "Windows_NT";
}

function warn(message) {
if (message) {
console.log(`\x1B[33;1m${message}\x1B[0m`);
}
}

module.exports = {
shouldSnapshot,
convertToUnixPath,
isWinOS
isWinOS,
warn
};
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
"minimatch": "3.0.4",
"nativescript-hook": "0.2.4",
"nativescript-worker-loader": "~0.9.0",
"properties-reader": "0.3.1",
"proxy-lib": "0.4.0",
"raw-loader": "~0.5.1",
"request": "2.88.0",
Expand Down
162 changes: 42 additions & 120 deletions snapshot/android/project-snapshot-generator.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,26 @@
const { dirname, isAbsolute, join, resolve, sep } = require("path");
const { existsSync, readFileSync, writeFileSync } = require("fs");
const { isAbsolute, join, resolve, sep } = require("path");
const { readFileSync, writeFileSync } = require("fs");

const shelljs = require("shelljs");
const semver = require("semver");

const SnapshotGenerator = require("./snapshot-generator");
const {
CONSTANTS,
createDirectory,
getJsonFile,
CONSTANTS
} = require("./utils");
const {
ANDROID_PROJECT_DIR,
ANDROID_APP_PATH,
ANDROID_CONFIGURATIONS_PATH,
getAndroidRuntimeVersion,
getAndroidV8Version,
getRuntimeNdkRevision,
getMksnapshotParams
} = require("../../androidProjectHelpers");

const MIN_ANDROID_RUNTIME_VERSION = "3.0.0";
// min version with settings.json file specifying the V8 version
const MIN_ANDROID_RUNTIME_VERSION = "5.2.1";
const VALID_ANDROID_RUNTIME_TAGS = Object.freeze(["next", "rc"]);
const V8_VERSIONS_FILE_NAME = "v8-versions.json";
const V8_VERSIONS_URL = `https://raw.githubusercontent.com/NativeScript/android-runtime/master/${V8_VERSIONS_FILE_NAME}`;
const V8_VERSIONS_LOCAL_PATH = resolve(CONSTANTS.SNAPSHOT_TMP_DIR, V8_VERSIONS_FILE_NAME);

const resolveRelativePath = (path) => {
if (path)
Expand Down Expand Up @@ -120,73 +117,6 @@ ProjectSnapshotGenerator.installSnapshotArtefacts = function (projectRoot) {
}
}

const versionIsPrerelease = version => version.indexOf("-") > -1;
const v8VersionsFileExists = () => existsSync(V8_VERSIONS_LOCAL_PATH);
const saveV8VersionsFile = versionsMap =>
writeFileSync(V8_VERSIONS_LOCAL_PATH, JSON.stringify(versionsMap));
const readV8VersionsFile = () => JSON.parse(readFileSync(V8_VERSIONS_LOCAL_PATH));
const fetchV8VersionsFile = () =>
new Promise((resolve, reject) => {
getJsonFile(V8_VERSIONS_URL)
.then(versionsMap => {
createDirectory(dirname(V8_VERSIONS_LOCAL_PATH));
saveV8VersionsFile(versionsMap);
return resolve(versionsMap);
})
.catch(reject);
});

const findV8Version = (runtimeVersion, v8VersionsMap) => {
const runtimeRange = Object.keys(v8VersionsMap)
.find(range => semver.satisfies(runtimeVersion, range));

return v8VersionsMap[runtimeRange];
}

const getV8VersionsMap = runtimeVersion =>
new Promise((resolve, reject) => {
if (!v8VersionsFileExists() || versionIsPrerelease(runtimeVersion)) {
fetchV8VersionsFile()
.then(versionsMap => resolve({ versionsMap, latest: true }))
.catch(reject);
} else {
const versionsMap = readV8VersionsFile();
return resolve({ versionsMap, latest: false });
}
});

ProjectSnapshotGenerator.prototype.getV8Version = function (generationOptions) {
return new Promise((resolve, reject) => {
const maybeV8Version = generationOptions.v8Version;
if (maybeV8Version) {
return resolve(maybeV8Version);
}

// try to get the V8 Version from the settings.json file in android runtime folder
const runtimeV8Version = getAndroidV8Version(this.options.projectRoot);
if (runtimeV8Version) {
return resolve(runtimeV8Version);
}

const runtimeVersion = getAndroidRuntimeVersion(this.options.projectRoot);
getV8VersionsMap(runtimeVersion)
.then(({ versionsMap, latest }) => {
const v8Version = findV8Version(runtimeVersion, versionsMap);

if (!v8Version && !latest) {
fetchV8VersionsFile().then(latestVersionsMap => {
const version = findV8Version(runtimeVersion, latestVersionsMap)
return resolve(version);
})
.catch(reject);
} else {
return resolve(v8Version);
}
})
.catch(reject);
});
}

ProjectSnapshotGenerator.prototype.validateAndroidRuntimeVersion = function () {
const currentRuntimeVersion = getAndroidRuntimeVersion(this.options.projectRoot);

Expand Down Expand Up @@ -224,53 +154,45 @@ ProjectSnapshotGenerator.prototype.generate = function (generationOptions) {

// Generate snapshots
const generator = new SnapshotGenerator({ buildPath: this.getBuildPath() });

const noV8VersionFoundMessage = `Cannot find suitable v8 version!`;
let shouldRethrow = false;

const mksnapshotParams = getMksnapshotParams(this.options.projectRoot);
const recommendedAndroidNdkRevision = getRuntimeNdkRevision(this.options.projectRoot);
const v8Version = generationOptions.v8Version || getAndroidV8Version(this.options.projectRoot);
if (!v8Version) {
throw new Error(noV8VersionFoundMessage);
}

return this.getV8Version(generationOptions).then(v8Version => {
shouldRethrow = true;
if (!v8Version) {
throw new Error(noV8VersionFoundMessage);
}
// NOTE: Order is important! Add new archs at the end of the array
const defaultTargetArchs = ["arm", "arm64", "ia32", "ia64"];
const runtimeVersion = getAndroidRuntimeVersion(this.options.projectRoot);
if (runtimeVersion && semver.lt(semver.coerce(runtimeVersion), "6.0.2")) {
const indexOfIa64 = defaultTargetArchs.indexOf("ia64");
// Before 6.0.2 version of Android runtime we supported only arm, arm64 and ia32.
defaultTargetArchs.splice(indexOfIa64, defaultTargetArchs.length - indexOfIa64);
}

// NOTE: Order is important! Add new archs at the end of the array
const defaultTargetArchs = ["arm", "arm64", "ia32", "ia64"];
const runtimeVersion = getAndroidRuntimeVersion(this.options.projectRoot);
if (runtimeVersion && semver.lt(semver.coerce(runtimeVersion), "6.0.2")) {
const indexOfIa64 = defaultTargetArchs.indexOf("ia64");
// Before 6.0.2 version of Android runtime we supported only arm, arm64 and ia32.
defaultTargetArchs.splice(indexOfIa64, defaultTargetArchs.length - indexOfIa64);
const options = {
snapshotToolsPath,
targetArchs: generationOptions.targetArchs || defaultTargetArchs,
v8Version: generationOptions.v8Version || v8Version,
preprocessedInputFile: generationOptions.preprocessedInputFile,
useLibs: generationOptions.useLibs || false,
inputFiles: generationOptions.inputFiles || [join(this.options.projectRoot, "__snapshot.js")],
androidNdkPath,
mksnapshotParams: mksnapshotParams,
snapshotInDocker: generationOptions.snapshotInDocker,
recommendedAndroidNdkRevision
};

return generator.generate(options).then(() => {
console.log("Snapshots build finished succesfully!");

if (generationOptions.install) {
ProjectSnapshotGenerator.cleanSnapshotArtefacts(this.options.projectRoot);
ProjectSnapshotGenerator.installSnapshotArtefacts(this.options.projectRoot);
console.log(generationOptions.useLibs ?
"Snapshot is included in the app as dynamically linked library (.so file)." :
"Snapshot is included in the app as binary .blob file. The more space-efficient option is to embed it in a dynamically linked library (.so file).");
}

const options = {
snapshotToolsPath,
targetArchs: generationOptions.targetArchs || defaultTargetArchs,
v8Version: generationOptions.v8Version || v8Version,
preprocessedInputFile: generationOptions.preprocessedInputFile,
useLibs: generationOptions.useLibs || false,
inputFiles: generationOptions.inputFiles || [join(this.options.projectRoot, "__snapshot.js")],
androidNdkPath,
mksnapshotParams: mksnapshotParams,
snapshotInDocker: generationOptions.snapshotInDocker
};

return generator.generate(options).then(() => {
console.log("Snapshots build finished succesfully!");

if (generationOptions.install) {
ProjectSnapshotGenerator.cleanSnapshotArtefacts(this.options.projectRoot);
ProjectSnapshotGenerator.installSnapshotArtefacts(this.options.projectRoot);
console.log(generationOptions.useLibs ?
"Snapshot is included in the app as dynamically linked library (.so file)." :
"Snapshot is included in the app as binary .blob file. The more space-efficient option is to embed it in a dynamically linked library (.so file).");
}
});
}).catch(error => {
throw shouldRethrow ?
error :
new Error(`${noV8VersionFoundMessage} Original error: ${error.message || error}`);
});
});;
}
58 changes: 53 additions & 5 deletions snapshot/android/snapshot-generator.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
const fs = require("fs");
const { dirname, relative, join, EOL } = require("path");
const child_process = require("child_process");
const { convertToUnixPath } = require("../../lib/utils");

const { convertToUnixPath, warn } = require("../../lib/utils");
const PropertiesReader = require('properties-reader');
const shelljs = require("shelljs");

const { createDirectory, downloadFile, getHostOS, getHostOSArch, CONSTANTS, has32BitArch, isMacOSCatalinaOrHigher, isSubPath } = require("./utils");
Expand Down Expand Up @@ -192,8 +192,9 @@ SnapshotGenerator.prototype.setupDocker = function () {
child_process.execSync(`docker pull ${SNAPSHOTS_DOCKER_IMAGE}`);
}

SnapshotGenerator.prototype.buildSnapshotLibs = function (androidNdkBuildPath, targetArchs) {
SnapshotGenerator.prototype.buildSnapshotLibs = function (androidNdkPath, recommendedAndroidNdkRevision, targetArchs) {
// Compile *.c files to produce *.so libraries with ndk-build tool
const androidNdkBuildPath = this.getAndroidNdkBuildPath(androidNdkPath, recommendedAndroidNdkRevision);
const ndkBuildPath = join(this.buildPath, "ndk-build");
const androidArchs = targetArchs.map(arch => this.convertToAndroidArchName(arch));
console.log("Building native libraries for " + androidArchs.join());
Expand All @@ -207,6 +208,54 @@ SnapshotGenerator.prototype.buildSnapshotLibs = function (androidNdkBuildPath, t
return join(ndkBuildPath, "libs");
}

SnapshotGenerator.prototype.getAndroidNdkBuildPath = function (androidNdkPath, recommendedAndroidNdkRevision) {
const ndkBuildExecutableName = "ndk-build";
// fallback for Android Runtime < 6.2.0 with the 6.1.0 value
recommendedAndroidNdkRevision = recommendedAndroidNdkRevision || "20.0.5594570";
let androidNdkBuildPath = "";
if (androidNdkPath) {
// specified by the user
const localNdkRevision = this.getAndroidNdkRevision(androidNdkPath);
androidNdkBuildPath = join(androidNdkPath, ndkBuildExecutableName);
if (!fs.existsSync(androidNdkBuildPath)) {
throw new Error(`The provided Android NDK path does not contain ${ndkBuildExecutableName} executable.`);
} else if (localNdkRevision !== recommendedAndroidNdkRevision) {
warn(`The provided Android NDK is v${localNdkRevision} while the recommended one is v${recommendedAndroidNdkRevision}`);
}
} else {
// available globally
let hasAndroidNdkInPath = true;
androidNdkBuildPath = ndkBuildExecutableName;
try {
child_process.execSync(`${androidNdkBuildPath} --version`);
console.log(`Cannot determine the version of the global Android NDK. The recommended versions is v${recommendedAndroidNdkRevision}`);
} catch (_) {
hasAndroidNdkInPath = false;
}

if (!hasAndroidNdkInPath) {
// installed in ANDROID_HOME
const androidHome = process.env.ANDROID_HOME;
androidNdkBuildPath = join(androidHome, "ndk", recommendedAndroidNdkRevision, ndkBuildExecutableName);
if (!fs.existsSync(androidNdkBuildPath)) {
throw new Error(`Android NDK v${recommendedAndroidNdkRevision} is not installed. You can find installation instructions in this article: https://developer.android.com/studio/projects/install-ndk#specific-version`);
}
}
}

return androidNdkBuildPath;
}

SnapshotGenerator.prototype.getAndroidNdkRevision = function (androidNdkPath) {
const ndkPropertiesFile = join(androidNdkPath, "source.properties");
if (fs.existsSync(ndkPropertiesFile)) {
const properties = PropertiesReader(ndkPropertiesFile);
return properties.get("Pkg.Revision");
} else {
return null;
}
}

SnapshotGenerator.prototype.buildIncludeGradle = function () {
shelljs.cp(INCLUDE_GRADLE_PATH, join(this.buildPath, "include.gradle"));
}
Expand Down Expand Up @@ -236,8 +285,7 @@ SnapshotGenerator.prototype.generate = function (options) {
).then(() => {
this.buildIncludeGradle();
if (options.useLibs) {
const androidNdkBuildPath = options.androidNdkPath ? join(options.androidNdkPath, "ndk-build") : "ndk-build";
this.buildSnapshotLibs(androidNdkBuildPath, options.targetArchs);
this.buildSnapshotLibs(options.androidNdkPath, options.recommendedAndroidNdkRevision, options.targetArchs);
}
return this.buildPath;
});
Expand Down
7 changes: 5 additions & 2 deletions templates/webpack.angular.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,11 @@ module.exports = env => {
unitTesting, // --env.unitTesting
verbose, // --env.verbose
snapshotInDocker, // --env.snapshotInDocker
skipSnapshotTools // --env.skipSnapshotTools
skipSnapshotTools, // --env.skipSnapshotTools
compileSnapshot // --env.compileSnapshot
} = env;

const useLibs = compileSnapshot;
const isAnySourceMapEnabled = !!sourceMap || !!hiddenSourceMap;
const externals = nsWebpack.getConvertedExternals(env.externals);
const appFullPath = resolve(projectRoot, appPath);
Expand Down Expand Up @@ -315,7 +317,8 @@ module.exports = env => {
projectRoot,
webpackConfig: config,
snapshotInDocker,
skipSnapshotTools
skipSnapshotTools,
useLibs
}));
}

Expand Down
7 changes: 5 additions & 2 deletions templates/webpack.javascript.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,11 @@ module.exports = env => {
unitTesting, // --env.unitTesting,
verbose, // --env.verbose
snapshotInDocker, // --env.snapshotInDocker
skipSnapshotTools // --env.skipSnapshotTools
skipSnapshotTools, // --env.skipSnapshotTools
compileSnapshot // --env.compileSnapshot
} = env;

const useLibs = compileSnapshot;
const isAnySourceMapEnabled = !!sourceMap || !!hiddenSourceMap;
const externals = nsWebpack.getConvertedExternals(env.externals);
const appFullPath = resolve(projectRoot, appPath);
Expand Down Expand Up @@ -253,7 +255,8 @@ module.exports = env => {
projectRoot,
webpackConfig: config,
snapshotInDocker,
skipSnapshotTools
skipSnapshotTools,
useLibs
}));
}

Expand Down
Loading