Skip to content

Commit

Permalink
feat!: unify & fix gradle library/tooling overrides (apache#1212)
Browse files Browse the repository at this point in the history
* enhancement: Control SDK versions and other default projects in one place
* fix: target/compile sdk usage
* refactor: cleanup gradle process
* chore: cleanup and remove unused changes
* chore: remove more unneeded FILE_PATH
* chore: fix lint error
* revert change intended to be part of a different PR
* chore: apply changes to revert to fit new changes
* fix: Ensure proper types
* breaking: Removed TempateFile class
  * Replaced the one and only usage of it with the properties-parser editor.
  * Breaking change because we are converting a method into an asynchronous method.
* refactor: Use the sync version of properties editor
* Gh 1178 fix sdk use gradlearg fix (apache#2)
* fix: readd gradleArg support
* fix: variable name
* refactor: remove unused mock variables
* Update bin/templates/cordova/lib/builders/ProjectBuilder.js
* Update bin/lib/create.js
* fix: const naming (review suggestion)
* fix: use defaults for framework building
* chore: apply review suggestion
* chore: rename config.json & defaults.json (review suggestions)
* refactor: updateUserProjectGradleConfig method
* refactor: minor changes in updateUserProjectGradleConfig
* refactor: major changes in updateUserProjectGradleConfig
* fix: wrong handling of missing preferences
* fix: usage of undefined this
* fix(create.spec): mocking of getPreference
* test(check_reqs): reduce diff size
* refactor: add wrapper to load gradle config defaults
* fix(check_reqs): get_target
  * Reads default SDK from default gradle config now
* fix(check_reqs.spec): return correct types from mocks
* revert to using get_target in create
* fix: e2e test

Co-authored-by: Erisu <ellis.bryan@gmail.com>
Co-authored-by: Raphael von der Grün <raphinesse@gmail.com>
  • Loading branch information
3 people authored and wedgberto committed May 17, 2022
1 parent 3c50d5f commit c820a47
Show file tree
Hide file tree
Showing 22 changed files with 268 additions and 254 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ example
/test/androidx/gradle
/test/androidx/gradlew
/test/androidx/gradlew.bat
/test/androidx/cdv-gradle-config.json

/test/assets/www/.tmp*
/test/assets/www/cordova.js
Expand Down
14 changes: 7 additions & 7 deletions bin/lib/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ var fs = require('fs-extra');
var utils = require('../templates/cordova/lib/utils');
var check_reqs = require('./../templates/cordova/lib/check_reqs');
var ROOT = path.join(__dirname, '..', '..');
const { createEditor } = require('properties-parser');

var CordovaError = require('cordova-common').CordovaError;
var AndroidManifest = require('../templates/cordova/lib/AndroidManifest');
Expand All @@ -42,16 +43,12 @@ function getFrameworkDir (projectPath, shared) {
return shared ? path.join(ROOT, 'framework') : path.join(projectPath, 'CordovaLib');
}

function copyJsAndLibrary (projectPath, shared, projectName, isLegacy) {
function copyJsAndLibrary (projectPath, shared, projectName, targetAPI) {
var nestedCordovaLibPath = getFrameworkDir(projectPath, false);
var srcCordovaJsPath = path.join(ROOT, 'bin', 'templates', 'project', 'assets', 'www', 'cordova.js');
var app_path = path.join(projectPath, 'app', 'src', 'main');
const platform_www = path.join(projectPath, 'platform_www');

if (isLegacy) {
app_path = projectPath;
}

fs.copySync(srcCordovaJsPath, path.join(app_path, 'assets', 'www', 'cordova.js'));

// Copy the cordova.js file to platforms/<platform>/platform_www/
Expand All @@ -69,11 +66,14 @@ function copyJsAndLibrary (projectPath, shared, projectName, isLegacy) {
} else {
fs.ensureDirSync(nestedCordovaLibPath);
fs.copySync(path.join(ROOT, 'framework', 'AndroidManifest.xml'), path.join(nestedCordovaLibPath, 'AndroidManifest.xml'));
fs.copySync(path.join(ROOT, 'framework', 'project.properties'), path.join(nestedCordovaLibPath, 'project.properties'));
const propertiesEditor = createEditor(path.join(ROOT, 'framework', 'project.properties'));
propertiesEditor.set('target', targetAPI);
propertiesEditor.save(path.join(nestedCordovaLibPath, 'project.properties'));
fs.copySync(path.join(ROOT, 'framework', 'build.gradle'), path.join(nestedCordovaLibPath, 'build.gradle'));
fs.copySync(path.join(ROOT, 'framework', 'cordova.gradle'), path.join(nestedCordovaLibPath, 'cordova.gradle'));
fs.copySync(path.join(ROOT, 'framework', 'repositories.gradle'), path.join(nestedCordovaLibPath, 'repositories.gradle'));
fs.copySync(path.join(ROOT, 'framework', 'src'), path.join(nestedCordovaLibPath, 'src'));
fs.copySync(path.join(ROOT, 'framework', 'cdv-gradle-config-defaults.json'), path.join(projectPath, 'cdv-gradle-config.json'));
}
}

Expand Down Expand Up @@ -277,7 +277,7 @@ exports.create = function (project_path, config, options, events) {
fs.ensureDirSync(path.join(app_path, 'libs'));

// copy cordova.js, cordova.jar
exports.copyJsAndLibrary(project_path, options.link, safe_activity_name);
exports.copyJsAndLibrary(project_path, options.link, safe_activity_name, target_api);

// Set up ther Android Studio paths
var java_path = path.join(app_path, 'java');
Expand Down
11 changes: 10 additions & 1 deletion bin/templates/cordova/lib/builders/ProjectBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -265,10 +265,11 @@ class ProjectBuilder {
}).then(function () {
return self.prepBuildFiles();
}).then(() => {
const config = this._getCordovaConfig();
// update/set the distributionUrl in the gradle-wrapper.properties
const gradleWrapperPropertiesPath = path.join(self.root, 'gradle/wrapper/gradle-wrapper.properties');
const gradleWrapperProperties = createEditor(gradleWrapperPropertiesPath);
const distributionUrl = process.env.CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL || 'https://services.gradle.org/distributions/gradle-6.8.3-all.zip';
const distributionUrl = process.env.CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL || `https://services.gradle.org/distributions/gradle-${config.GRADLE_VERSION}-all.zip`;
gradleWrapperProperties.set('distributionUrl', distributionUrl);
gradleWrapperProperties.save();

Expand All @@ -287,6 +288,14 @@ class ProjectBuilder {
});
}

/**
* @private
* @returns The user defined configs
*/
_getCordovaConfig () {
return fs.readJSONSync(path.join(this.root, 'cdv-gradle-config.json'));
}

/*
* Builds the project with gradle.
* Returns a promise.
Expand Down
43 changes: 13 additions & 30 deletions bin/templates/cordova/lib/check_reqs.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,10 @@ var fs = require('fs-extra');
const { forgivingWhichSync, isWindows, isDarwin } = require('./utils');
const java = require('./env/java');
var REPO_ROOT = path.join(__dirname, '..', '..', '..', '..');
var PROJECT_ROOT = path.join(__dirname, '..', '..');
const { CordovaError, ConfigParser, events } = require('cordova-common');
var android_sdk = require('./android_sdk');
const { createEditor } = require('properties-parser');
const semver = require('semver');
const { SDK_VERSION } = require('./gradle-config-defaults');

const EXPECTED_JAVA_VERSION = '1.8.x';

Expand All @@ -36,44 +35,28 @@ const EXPECTED_JAVA_VERSION = '1.8.x';
Object.assign(module.exports, { isWindows, isDarwin });

/**
* @description Get valid target from framework/project.properties if run from this repo
* Otherwise get target from project.properties file within a generated cordova-android project
* @returns {string} The android target in format "android-${target}"
*/
module.exports.get_target = function () {
const projectPropertiesPaths = [
path.join(REPO_ROOT, 'framework', 'project.properties'),
path.join(PROJECT_ROOT, 'project.properties')
];

// Get the minimum required target API from the framework.
let target = projectPropertiesPaths.filter(filePath => fs.existsSync(filePath))
.map(filePath => createEditor(filePath).get('target'))
.pop();
const userTargetSdkVersion = getUserTargetSdkVersion();

if (!target) {
throw new Error(`We could not locate the target from the "project.properties" at either "${projectPropertiesPaths.join('", "')}".`);
if (userTargetSdkVersion && userTargetSdkVersion < SDK_VERSION) {
events.emit('warn', `android-targetSdkVersion should be greater than or equal to ${SDK_VERSION}.`);
}

return `android-${Math.max(userTargetSdkVersion, SDK_VERSION)}`;
};

/** @returns {number} target sdk or 0 if undefined */
function getUserTargetSdkVersion () {
// If the repo config.xml file exists, find the desired targetSdkVersion.
const configFile = path.join(REPO_ROOT, 'config.xml');
if (!fs.existsSync(configFile)) return target;
if (!fs.existsSync(configFile)) return 0;

const configParser = new ConfigParser(configFile);
const desiredAPI = parseInt(configParser.getPreference('android-targetSdkVersion', 'android'), 10);

if (!isNaN(desiredAPI)) {
const minimumAPI = parseInt(target.split('-').pop(), 10);

if (desiredAPI >= minimumAPI) {
target = `android-${desiredAPI}`;
} else {
events.emit('warn', `android-targetSdkVersion should be greater than or equal to ${minimumAPI}.`);
}
}

return target;
};
const targetSdkVersion = parseInt(configParser.getPreference('android-targetSdkVersion', 'android'), 10);
return isNaN(targetSdkVersion) ? 0 : targetSdkVersion;
}

module.exports.get_gradle_wrapper = function () {
var androidStudioPath;
Expand Down
30 changes: 30 additions & 0 deletions bin/templates/cordova/lib/gradle-config-defaults.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*!
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/

const ABS_MODULE_PATH = '/framework/cdv-gradle-config-defaults.json';

try {
// Try relative require first, …
const REPO_ROOT = '../../../..';
module.exports = require(REPO_ROOT + ABS_MODULE_PATH);
} catch (error) {
// … then fall back to installed-package require
if (error.code !== 'MODULE_NOT_FOUND') throw error;
module.exports = require('cordova-android' + ABS_MODULE_PATH);
}
4 changes: 2 additions & 2 deletions bin/templates/cordova/lib/plugin-build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ dependencies {
}

android {
compileSdkVersion cdvCompileSdkVersion
buildToolsVersion cdvBuildToolsVersion
compileSdkVersion cordovaConfig.SDK_VERSION
buildToolsVersion cordovaConfig.BUILD_TOOLS_VERSION

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_6
Expand Down
97 changes: 75 additions & 22 deletions bin/templates/cordova/lib/prepare.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ var PlatformJson = require('cordova-common').PlatformJson;
var PlatformMunger = require('cordova-common').ConfigChanges.PlatformMunger;
var PluginInfoProvider = require('cordova-common').PluginInfoProvider;
const utils = require('./utils');
const gradleConfigDefaults = require('./gradle-config-defaults');

const GradlePropertiesParser = require('./config/GradlePropertiesParser');

Expand All @@ -54,29 +55,11 @@ module.exports.prepare = function (cordovaProject, options) {

this._config = updateConfigFilesFrom(cordovaProject.projectConfig, munger, this.locations);

// Get the min SDK version from config.xml
const minSdkVersion = this._config.getPreference('android-minSdkVersion', 'android');
const maxSdkVersion = this._config.getPreference('android-maxSdkVersion', 'android');
const targetSdkVersion = this._config.getPreference('android-targetSdkVersion', 'android');
const isGradlePluginKotlinEnabled = this._config.getPreference('GradlePluginKotlinEnabled', 'android');
const gradlePluginKotlinCodeStyle = this._config.getPreference('GradlePluginKotlinCodeStyle', 'android');
const androidXAppCompatVersion = this._config.getPreference('AndroidXAppCompatVersion', 'android');

const gradlePropertiesUserConfig = {};
if (minSdkVersion) gradlePropertiesUserConfig.cdvMinSdkVersion = minSdkVersion;
if (maxSdkVersion) gradlePropertiesUserConfig.cdvMaxSdkVersion = maxSdkVersion;
if (targetSdkVersion) gradlePropertiesUserConfig.cdvTargetSdkVersion = targetSdkVersion;
if (args.jvmargs) gradlePropertiesUserConfig['org.gradle.jvmargs'] = args.jvmargs;
if (isGradlePluginKotlinEnabled) {
gradlePropertiesUserConfig['kotlin.code.style'] = gradlePluginKotlinCodeStyle || 'official';
}

if (androidXAppCompatVersion) {
gradlePropertiesUserConfig.cdvAndroidXAppCompatVersion = androidXAppCompatVersion;
}
// Update Gradle cdv-gradle-config.json
updateUserProjectGradleConfig(this);

const gradlePropertiesParser = new GradlePropertiesParser(this.locations.root);
gradlePropertiesParser.configure(gradlePropertiesUserConfig);
// Update Project's Gradle Properties
updateUserProjectGradlePropertiesConfig(this, args);

// Update own www dir with project's www assets and plugins' assets and js-files
return Promise.resolve(updateWww(cordovaProject, this.locations)).then(function () {
Expand All @@ -91,6 +74,76 @@ module.exports.prepare = function (cordovaProject, options) {
});
};

/** @param {PlatformApi} project */
function updateUserProjectGradleConfig (project) {
// Generate project gradle config
const projectGradleConfig = {
...gradleConfigDefaults,
...getUserGradleConfig(project._config)
};

// Write out changes
const projectGradleConfigPath = path.join(project.root, 'cdv-gradle-config.json');
fs.writeJSONSync(projectGradleConfigPath, projectGradleConfig, { spaces: 2 });
}

function getUserGradleConfig (configXml) {
const configXmlToGradleMapping = [
{ xmlKey: 'android-minSdkVersion', gradleKey: 'MIN_SDK_VERSION', type: Number },
{ xmlKey: 'android-maxSdkVersion', gradleKey: 'MAX_SDK_VERSION', type: Number },
{ xmlKey: 'android-targetSdkVersion', gradleKey: 'SDK_VERSION', type: Number },
{ xmlKey: 'android-buildToolsVersion', gradleKey: 'BUILD_TOOLS_VERSION', type: String },
{ xmlKey: 'GradleVersion', gradleKey: 'GRADLE_VERSION', type: String },
{ xmlKey: 'AndroidGradlePluginVersion', gradleKey: 'AGP_VERSION', type: String },
{ xmlKey: 'GradlePluginKotlinVersion', gradleKey: 'KOTLIN_VERSION', type: String },
{ xmlKey: 'AndroidXAppCompatVersion', gradleKey: 'ANDROIDX_APP_COMPAT_VERSION', type: String },
{ xmlKey: 'GradlePluginGoogleServicesVersion', gradleKey: 'GRADLE_PLUGIN_GOOGLE_SERVICES_VERSION', type: String },
{ xmlKey: 'GradlePluginGoogleServicesEnabled', gradleKey: 'IS_GRADLE_PLUGIN_GOOGLE_SERVICES_ENABLED', type: Boolean },
{ xmlKey: 'GradlePluginKotlinEnabled', gradleKey: 'IS_GRADLE_PLUGIN_KOTLIN_ENABLED', type: Boolean }
];

return configXmlToGradleMapping.reduce((config, mapping) => {
const rawValue = configXml.getPreference(mapping.xmlKey, 'android');

// ignore missing preferences (which occur as '')
if (rawValue) {
config[mapping.gradleKey] = parseStringAsType(rawValue, mapping.type);
}

return config;
}, {});
}

/** Converts given string to given type */
function parseStringAsType (value, type) {
switch (type) {
case String:
return String(value);
case Number:
return parseFloat(value);
case Boolean:
return value.toLowerCase() === 'true';
default:
throw new CordovaError('Invalid type: ' + type);
}
}

function updateUserProjectGradlePropertiesConfig (project, args) {
const gradlePropertiesUserConfig = {};

// Get the min SDK version from config.xml
if (args.jvmargs) gradlePropertiesUserConfig['org.gradle.jvmargs'] = args.jvmargs;

const isGradlePluginKotlinEnabled = project._config.getPreference('GradlePluginKotlinEnabled', 'android');
if (isGradlePluginKotlinEnabled) {
const gradlePluginKotlinCodeStyle = project._config.getPreference('GradlePluginKotlinCodeStyle', 'android');
gradlePropertiesUserConfig['kotlin.code.style'] = gradlePluginKotlinCodeStyle || 'official';
}

const gradlePropertiesParser = new GradlePropertiesParser(project.root);
gradlePropertiesParser.configure(gradlePropertiesUserConfig);
}

module.exports.clean = function (options) {
// A cordovaProject isn't passed into the clean() function, because it might have
// been called from the platform shell script rather than the CLI. Check for the
Expand Down

0 comments on commit c820a47

Please sign in to comment.