Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: API 34 Support #1678

Merged
merged 18 commits into from
May 13, 2024
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
*.scm text
*.sql text
*.sh text
*.bat text
*.bat text eol=crlf

# templates
*.ejs text
Expand Down Expand Up @@ -92,3 +92,4 @@ AUTHORS text
*.woff binary
*.pyc binary
*.pdf binary
*.jar binary
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:

strategy:
matrix:
node-version: [16.x, 18.x, 20.x]
node-version: [16.x, 18.x, 20.x, 22.x]
os: [ubuntu-latest, windows-latest, macos-latest]

steps:
Expand All @@ -39,7 +39,7 @@ jobs:
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '11'
java-version: '17'

- name: Environment Information
run: |
Expand Down
6 changes: 1 addition & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,8 @@ example
**/assets/www/cordova.js

/test/.externalNativeBuild

/test/androidx/gradle
/test/androidx/gradlew
/test/androidx/gradlew.bat
/test/androidx/cdv-gradle-config.json

/test/androidx/tools
/test/assets/www/.tmp*
/test/assets/www/cordova.js
/test/bin
Expand Down
3 changes: 3 additions & 0 deletions .ratignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ intermediates
reports
test-results
node_modules
gradle
gradlew
gradlew.bat
3 changes: 2 additions & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.

Copyright 2015-2020 Apache Cordova
Copyright 2015-2024 Apache Cordova

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand All @@ -200,3 +200,4 @@
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.

4 changes: 2 additions & 2 deletions framework/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ android {
buildToolsVersion cordovaConfig.BUILD_TOOLS_VERSION

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaLanguageVersion.of(cordovaConfig.JAVA_SOURCE_COMPATIBILITY)
targetCompatibility JavaLanguageVersion.of(cordovaConfig.JAVA_TARGET_COMPATIBILITY)
}

// For the Android Cordova Lib, we allow changing the minSdkVersion, but it is at the users own risk
Expand Down
13 changes: 8 additions & 5 deletions framework/cdv-gradle-config-defaults.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
{
"MIN_SDK_VERSION": 24,
"SDK_VERSION": 33,
"SDK_VERSION": 34,
"COMPILE_SDK_VERSION": null,
"GRADLE_VERSION": "7.6",
"MIN_BUILD_TOOLS_VERSION": "33.0.2",
"AGP_VERSION": "7.4.2",
"GRADLE_VERSION": "8.7",
"MIN_BUILD_TOOLS_VERSION": "34.0.0",
"AGP_VERSION": "8.3.0",
"KOTLIN_VERSION": "1.7.21",
"ANDROIDX_APP_COMPAT_VERSION": "1.6.1",
"ANDROIDX_WEBKIT_VERSION": "1.6.0",
"ANDROIDX_CORE_SPLASHSCREEN_VERSION": "1.0.0",
"GRADLE_PLUGIN_GOOGLE_SERVICES_VERSION": "4.3.15",
"IS_GRADLE_PLUGIN_GOOGLE_SERVICES_ENABLED": false,
"IS_GRADLE_PLUGIN_KOTLIN_ENABLED": false,
"PACKAGE_NAMESPACE": "io.cordova.helloCordova"
"PACKAGE_NAMESPACE": "io.cordova.helloCordova",
"JAVA_SOURCE_COMPATIBILITY": 8,
"JAVA_TARGET_COMPATIBILITY": 8,
"KOTLIN_JVM_TARGET": null
}
56 changes: 28 additions & 28 deletions lib/builders/ProjectBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const events = require('cordova-common').events;
const CordovaError = require('cordova-common').CordovaError;
const check_reqs = require('../check_reqs');
const PackageType = require('../PackageType');
const { compareByAll } = require('../utils');
const { compareByAll, isWindows } = require('../utils');
const { createEditor } = require('properties-parser');
const CordovaGradleConfigParserFactory = require('../config/CordovaGradleConfigParserFactory');

Expand Down Expand Up @@ -85,9 +85,7 @@ class ProjectBuilder {
}

getArgs (cmd, opts) {
let args = [
'-b', path.join(this.root, 'build.gradle')
];
let args = [];
if (opts.extraArgs) {
args = args.concat(opts.extraArgs);
}
Expand Down Expand Up @@ -116,16 +114,27 @@ class ProjectBuilder {
return args;
}

/*
* This returns a promise
*/
runGradleWrapper (gradle_cmd) {
const gradlePath = path.join(this.root, 'gradlew');
const wrapperGradle = path.join(this.root, 'wrapper.gradle');
if (fs.existsSync(gradlePath)) {
// Literally do nothing, for some reason this works, while !fs.existsSync didn't on Windows
getGradleWrapperPath () {
let wrapper = path.join(this.root, 'tools', 'gradlew');

if (isWindows()) {
wrapper += '.bat';
}

return wrapper;
}

/**
* Installs/updates the gradle wrapper
* @param {string} gradleVersion The gradle version to install. Ignored if CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL environment variable is defined
* @returns {Promise<void>}
*/
async installGradleWrapper (gradleVersion) {
if (process.env.CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL) {
events.emit('verbose', `Overriding Gradle Version via CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL (${process.env.CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL})`);
await execa('gradle', ['-p', path.join(this.root, 'tools'), 'wrapper', '--gradle-distribution-url', process.env.CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL], { stdio: 'inherit' });
} else {
return execa(gradle_cmd, ['-p', this.root, 'wrapper', '-b', wrapperGradle], { stdio: 'inherit' });
await execa('gradle', ['-p', path.join(this.root, 'tools'), 'wrapper', '--gradle-version', gradleVersion], { stdio: 'inherit' });
}
}

Expand Down Expand Up @@ -275,23 +284,14 @@ class ProjectBuilder {

prepEnv (opts) {
const self = this;
const config = this._getCordovaConfig();
return check_reqs.check_gradle()
.then(function (gradlePath) {
return self.runGradleWrapper(gradlePath);
.then(function () {
events.emit('verbose', `Using Gradle: ${config.GRADLE_VERSION}`);
return self.installGradleWrapper(config.GRADLE_VERSION);
}).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-${config.GRADLE_VERSION}-all.zip`;
gradleWrapperProperties.set('distributionUrl', distributionUrl);
gradleWrapperProperties.save();

events.emit('verbose', `Gradle Distribution URL: ${distributionUrl}`);
})
.then(() => {
const signingPropertiesPath = path.join(self.root, `${opts.buildType}${SIGNING_PROPERTIES}`);

if (fs.existsSync(signingPropertiesPath)) fs.removeSync(signingPropertiesPath);
Expand All @@ -317,7 +317,7 @@ class ProjectBuilder {
* Returns a promise.
*/
async build (opts) {
const wrapper = path.join(this.root, 'gradlew');
const wrapper = this.getGradleWrapperPath();
const args = this.getArgs(opts.buildType === 'debug' ? 'debug' : 'release', opts);

events.emit('verbose', `Running Gradle Build: ${wrapper} ${args.join(' ')}`);
Expand All @@ -338,7 +338,7 @@ class ProjectBuilder {
}

clean (opts) {
const wrapper = path.join(this.root, 'gradlew');
const wrapper = this.getGradleWrapperPath();
const args = this.getArgs('clean', opts);
return execa(wrapper, args, { stdio: 'inherit', cwd: path.resolve(this.root) })
.then(() => {
Expand Down
25 changes: 13 additions & 12 deletions lib/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,20 +113,15 @@ function prepBuildFiles (projectPath) {
buildModule.getBuilder(projectPath).prepBuildFiles();
}

function copyBuildRules (projectPath, isLegacy) {
function copyBuildRules (projectPath) {
const srcDir = path.join(ROOT, 'templates', 'project');

if (isLegacy) {
// The project's build.gradle is identical to the earlier build.gradle, so it should still work
fs.copySync(path.join(srcDir, 'legacy', 'build.gradle'), path.join(projectPath, 'legacy', 'build.gradle'));
fs.copySync(path.join(srcDir, 'wrapper.gradle'), path.join(projectPath, 'wrapper.gradle'));
} else {
fs.copySync(path.join(srcDir, 'build.gradle'), path.join(projectPath, 'build.gradle'));
fs.copySync(path.join(srcDir, 'app', 'build.gradle'), path.join(projectPath, 'app', 'build.gradle'));
fs.copySync(path.join(srcDir, 'app', 'repositories.gradle'), path.join(projectPath, 'app', 'repositories.gradle'));
fs.copySync(path.join(srcDir, 'repositories.gradle'), path.join(projectPath, 'repositories.gradle'));
fs.copySync(path.join(srcDir, 'wrapper.gradle'), path.join(projectPath, 'wrapper.gradle'));
}
fs.copySync(path.join(srcDir, 'build.gradle'), path.join(projectPath, 'build.gradle'));
fs.copySync(path.join(srcDir, 'app', 'build.gradle'), path.join(projectPath, 'app', 'build.gradle'));
fs.copySync(path.join(srcDir, 'app', 'repositories.gradle'), path.join(projectPath, 'app', 'repositories.gradle'));
fs.copySync(path.join(srcDir, 'repositories.gradle'), path.join(projectPath, 'repositories.gradle'));

copyGradleTools(projectPath);
}

function copyScripts (projectPath) {
Expand Down Expand Up @@ -176,6 +171,12 @@ function validateProjectName (project_name) {
return Promise.resolve();
}

function copyGradleTools (projectPath) {
const srcDir = path.join(ROOT, 'templates', 'project');

fs.copySync(path.resolve(srcDir, 'tools'), path.resolve(projectPath, 'tools'));
}

/**
* Creates an android application with the given options.
*
Expand Down
5 changes: 4 additions & 1 deletion lib/prepare.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,10 @@ function getUserGradleConfig (configXml) {
{ xmlKey: 'AndroidXWebKitVersion', gradleKey: 'ANDROIDX_WEBKIT_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 }
{ xmlKey: 'GradlePluginKotlinEnabled', gradleKey: 'IS_GRADLE_PLUGIN_KOTLIN_ENABLED', type: Boolean },
{ xmlKey: 'AndroidJavaSourceCompatibility', gradleKey: 'JAVA_SOURCE_COMPATIBILITY', type: Number },
{ xmlKey: 'AndroidJavaTargetCompatibility', gradleKey: 'JAVA_TARGET_COMPATIBILITY', type: Number },
{ xmlKey: 'AndroidKotlinJVMTarget', gradleKey: 'KOTLIN_JVM_TARGET', type: String }
erisu marked this conversation as resolved.
Show resolved Hide resolved
];

return configXmlToGradleMapping.reduce((config, mapping) => {
Expand Down
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@
"unit-tests": "jasmine --config=spec/unit/jasmine.json",
"cover": "nyc jasmine --config=spec/coverage.json",
"e2e-tests": "jasmine --config=spec/e2e/jasmine.json",
"java-unit-tests": "node test/run_java_unit_tests.js",
"clean:java-unit-tests": "node test/clean.js"
"java-unit-tests": "node test/run_java_unit_tests.js"
},
"author": "Apache Software Foundation",
"license": "Apache-2.0",
Expand Down
38 changes: 27 additions & 11 deletions spec/unit/builders/ProjectBuilder.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
const fs = require('fs-extra');
const path = require('path');
const rewire = require('rewire');
const { isWindows } = require('../../../lib/utils');

describe('ProjectBuilder', () => {
const rootDir = '/root';
Expand Down Expand Up @@ -128,19 +129,24 @@ describe('ProjectBuilder', () => {
});
});

describe('runGradleWrapper', () => {
it('should run the provided gradle command if a gradle wrapper does not already exist', () => {
spyOn(fs, 'existsSync').and.returnValue(false);
builder.runGradleWrapper('/my/sweet/gradle');
expect(execaSpy).toHaveBeenCalledWith('/my/sweet/gradle', jasmine.any(Array), jasmine.any(Object));
describe('installGradleWrapper', () => {
beforeEach(() => {
execaSpy.and.resolveTo();
});

it('should do nothing if a gradle wrapper exists in the project directory', () => {
spyOn(fs, 'existsSync').and.returnValue(true);
builder.runGradleWrapper('/my/sweet/gradle');
expect(execaSpy).not.toHaveBeenCalledWith('/my/sweet/gradle', jasmine.any(Array), jasmine.any(Object));
it('should run gradle wrapper 8.7', async () => {
await builder.installGradleWrapper('8.7');
expect(execaSpy).toHaveBeenCalledWith('gradle', ['-p', path.normalize('/root/tools'), 'wrapper', '--gradle-version', '8.7'], jasmine.any(Object));
});

it('CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL should override gradle version', async () => {
process.env.CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL = 'https://dist.local';
await builder.installGradleWrapper('8.7');
delete process.env.CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL;
expect(execaSpy).toHaveBeenCalledWith('gradle', ['-p', path.normalize('/root/tools'), 'wrapper', '--gradle-distribution-url', 'https://dist.local'], jasmine.any(Object));
});
});

describe('build', () => {
beforeEach(() => {
spyOn(builder, 'getArgs');
Expand Down Expand Up @@ -170,7 +176,12 @@ describe('ProjectBuilder', () => {

builder.build({});

expect(execaSpy).toHaveBeenCalledWith(path.join(rootDir, 'gradlew'), testArgs, jasmine.anything());
let gradle = path.join(rootDir, 'tools', 'gradlew');
if (isWindows()) {
gradle += '.bat';
}

expect(execaSpy).toHaveBeenCalledWith(gradle, testArgs, jasmine.anything());
});

it('should reject if the spawn fails', () => {
Expand Down Expand Up @@ -227,8 +238,13 @@ describe('ProjectBuilder', () => {
const gradleArgs = ['test', 'args', '-f'];
builder.getArgs.and.returnValue(gradleArgs);

let gradle = path.join(rootDir, 'tools', 'gradlew');
if (isWindows()) {
gradle += '.bat';
}

return builder.clean(opts).then(() => {
expect(execaSpy).toHaveBeenCalledWith(path.join(rootDir, 'gradlew'), gradleArgs, jasmine.anything());
expect(execaSpy).toHaveBeenCalledWith(gradle, gradleArgs, jasmine.anything());
});
});

Expand Down
39 changes: 37 additions & 2 deletions templates/project/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,10 @@ task cdvPrintProps {
android {
namespace cordovaConfig.PACKAGE_NAMESPACE

buildFeatures {
buildConfig true
}

defaultConfig {
versionCode cdvVersionCode ?: new BigInteger("" + privateHelpers.extractIntFromManifest("versionCode"))
applicationId cordovaConfig.PACKAGE_NAMESPACE
Expand Down Expand Up @@ -248,8 +252,39 @@ android {
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaLanguageVersion.of(cordovaConfig.JAVA_SOURCE_COMPATIBILITY)
targetCompatibility JavaLanguageVersion.of(cordovaConfig.JAVA_TARGET_COMPATIBILITY)
}

if (cordovaConfig.IS_GRADLE_PLUGIN_KOTLIN_ENABLED) {
if (cordovaConfig.KOTLIN_JVM_TARGET == null) {
// If the value is null, fallback to JAVA_TARGET_COMPATIBILITY,
// as they generally should be equal
def javaTarget = JavaLanguageVersion.of(cordovaConfig.JAVA_TARGET_COMPATIBILITY)

// check if javaTarget is <= 8; if so, we need to prefix it with "1."
// Starting with 9 and later, the value can be used as is.
if (javaTarget.compareTo(JavaLanguageVersion.of(8)) <= 0) {
javaTarget = "1." + javaTarget
}

cordovaConfig.KOTLIN_JVM_TARGET = javaTarget
}

// Similar to above, check if kotlin target is <= 8, if so prefix it.
// This allows the user to use consistent set of values in config.xml
// Rather than having to be aware whether the "1."" prefix is needed.
// This check is only done if the value isn't already prefixed with 1.
if (
!cordovaConfig.KOTLIN_JVM_TARGET.startsWith("1.") &&
JavaLanguageVersion.of(cordovaConfig.KOTLIN_JVM_TARGET).compareTo(JavaLanguageVersion.of(8)) <= 0
) {
cordovaConfig.KOTLIN_JVM_TARGET = "1." + cordovaConfig.KOTLIN_JVM_TARGET
}

kotlinOptions {
jvmTarget = cordovaConfig.KOTLIN_JVM_TARGET
}
}

if (cdvReleaseSigningPropertiesFile) {
Expand Down