Skip to content

Commit

Permalink
[Android] Expand Java GN rules to support more complex usecases (#9876)
Browse files Browse the repository at this point in the history
* Amend Java build rules to support prebuilt JARs and using Java targets in deps.

* Test Java GN build rules
  • Loading branch information
austinh0 authored Sep 24, 2021
1 parent 3647c29 commit c9c3b65
Show file tree
Hide file tree
Showing 25 changed files with 594 additions and 73 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/android.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,7 @@ jobs:
run: |
./scripts/run_in_build_env.sh \
"./scripts/build/build_examples.py --no-log-timestamps --platform android build"
- name: Run Android build rule tests
run: |
./scripts/run_in_build_env.sh \
"ninja -C out/android-arm64-chip_tool build/chip/java/tests:java_build_test.tests"
3 changes: 3 additions & 0 deletions BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ if (current_toolchain != "${dir_pw_toolchain}/default:default") {

if (chip_build_tests) {
deps += [ "//src:tests" ]
if (current_os == "android") {
deps += [ "${chip_root}/build/chip/java/tests:java_build_test" ]
}
}

if (chip_with_lwip) {
Expand Down
30 changes: 28 additions & 2 deletions build/chip/java/javac_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"""Wrapper script to run javac command as an action with gn."""

import argparse
import json
import os
import subprocess
import sys
Expand Down Expand Up @@ -78,6 +79,19 @@ def FindCommand(command):
return None


def ReadBuildConfig(build_config):
with open(build_config, 'r') as file:
return json.load(file)


def ComputeClasspath(build_config_json):
unique_jars = build_config_json['deps_info']['deps_jars']
if sys.platform == 'win32':
return ";".join(unique_jars)
else:
return ":".join(unique_jars)


def main():
java_path = FindCommand('javac')
if not java_path:
Expand All @@ -95,13 +109,25 @@ def main():
dest='outfile',
required=True,
help='Output file containing a list of classes')
parser.add_argument(
'--build-config',
dest='build_config',
required=True,
help='Build config')
parser.add_argument(
'rest', metavar='JAVAC_ARGS', nargs='*', help='Argumets to pass to javac')

args = parser.parse_args()
if not os.path.isdir(args.classdir):
os.makedirs(args.classdir)
retcode = subprocess.check_call([java_path] + args.rest)
os.makedirs(args.classdir, exist_ok=True)

build_config_json = ReadBuildConfig(args.build_config)
classpath = ComputeClasspath(build_config_json)
java_args = [java_path]
if classpath:
java_args += ["-classpath", classpath]

retcode = subprocess.check_call(java_args + args.rest)
if retcode != EXIT_SUCCESS:
return retcode

Expand Down
174 changes: 121 additions & 53 deletions build/chip/java/rules.gni
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,15 @@ import("${chip_root}/build/config/android/config.gni")

javac_runner = "${chip_root}/build/chip/java/javac_runner.py"
jar_runner = "${chip_root}/build/chip/java/jar_runner.py"
write_build_config = "${chip_root}/build/chip/java/write_build_config.py"

assert(android_sdk_root != "", "android_sdk_root must be specified")

# Declare a java library target
#
# sources: List of .java files included in this library.
# sources: List of .java files included in this library. Mutually exclusive with jar_path.
#
# jar_path: A path to an existing JAR. Mutually exclusive with sources.
#
# output_name: File name for the output .jar (not including extension).
# Defaults to the input .jar file name.
Expand All @@ -39,76 +42,137 @@ template("java_library") {
_jar_name += ".jar"
}

# Additional flags
_javac_flags = [
"-Werror",
"-Xlint:all",
]
if (defined(invoker.javac_flags)) {
_javac_flags += invoker.javac_flags
}

_deps = []
if (defined(invoker.deps)) {
_deps = invoker.deps
}

_data_deps = []
if (defined(invoker.data_deps)) {
_data_deps = invoker.data_deps
# What files will be compiled
_java_files = []
if (defined(invoker.sources)) {
_java_files = invoker.sources
}

# What files will be compiled
_java_files = invoker.sources
_is_prebuilt = defined(invoker.jar_path)

_jar_output = ""
_target_dir_name = get_label_info(":$target_name", "dir")
if (_is_prebuilt) {
assert(_java_files == [])
_jar_output = "$root_out_dir/lib/$_target_dir_name/" +
get_path_info(invoker.jar_path, "name") + ".jar"
} else {
_jar_output = "$root_out_dir/lib/$_target_dir_name/$_jar_name"
}

# Generates a .java file containing all sources to be compiled
_java_sources_file = "$target_gen_dir/$target_name.sources"
if (defined(invoker.java_sources_file)) {
_java_sources_file = invoker.java_sources_file
# Generate a list containing the expected build_config filepath of every dependency.
_deps_configs = []
foreach(_dep, _deps) {
_dep_gen_dir = get_label_info(_dep, "target_gen_dir")
_dep_name = get_label_info(_dep, "name")
_dep_config = "$_dep_gen_dir/$_dep_name.json"
_deps_configs += [ _dep_config ]
}
write_file(_java_sources_file, rebase_path(_java_files, root_build_dir))
_rebased_deps_configs = rebase_path(_deps_configs, root_build_dir)

# Compiles the given files into a directory and generates a 'class list'
_javac_target_name = target_name + "__javac"
_class_dir = rebase_path(target_out_dir, root_build_dir) + "/classes"
_class_list_file = "$target_gen_dir/$target_name.classlist"
action(_javac_target_name) {
sources = _java_files
# Create the name for this target's build_config.
_library_target_name = target_name
_build_config = "$target_gen_dir/$_library_target_name.json"
_rebased_build_config = rebase_path(_build_config, root_build_dir)

outputs = [ _class_list_file ]
# Write the build_config file for this target.
_config_target_name = target_name + "_config"
action(_config_target_name) {
script = write_build_config

script = javac_runner
deps = _deps

outputs = [ _build_config ]
args = [
"--classdir",
_class_dir,
"--outfile",
rebase_path(_class_list_file, root_build_dir),
"--",
"-d",
_class_dir,
"@" + rebase_path(_java_sources_file, root_build_dir),
] + _javac_flags
"--jar-path",
rebase_path(_jar_output, root_build_dir),
"--build-config",
_rebased_build_config,
"--deps-configs=$_rebased_deps_configs",
]
}

# Bundles all files within the 'class directory' into a jar file
_jar_output = "$root_out_dir/lib/$_jar_name"
action(target_name) {
deps = [ ":$_javac_target_name" ] + _deps
# Building from sources - perform Java compilation and JAR creation.
if (!_is_prebuilt) {
# Additional flags
_javac_flags = [
"-Werror",
"-Xlint:all",
]
if (defined(invoker.javac_flags)) {
_javac_flags += invoker.javac_flags
}

data_deps = _data_deps
# Data deps
_data_deps = []
if (defined(invoker.data_deps)) {
_data_deps = invoker.data_deps
}

outputs = [ _jar_output ]
# Generates a .java file containing all sources to be compiled
_java_sources_file = "$target_gen_dir/$target_name.sources"
if (defined(invoker.java_sources_file)) {
_java_sources_file = invoker.java_sources_file
}
write_file(_java_sources_file, rebase_path(_java_files, root_build_dir))

# Compiles the given files into a directory and generates a 'class list'
_javac_target_name = target_name + "__javac"
_class_dir = rebase_path(target_out_dir, root_build_dir) + "/classes"
_class_list_file = "$target_gen_dir/$target_name.classlist"
action(_javac_target_name) {
sources = _java_files
deps = [ ":$_config_target_name" ]

outputs = [ _class_list_file ]

script = javac_runner

args = [
"--classdir",
_class_dir,
"--outfile",
rebase_path(_class_list_file, root_build_dir),
"--build-config",
_rebased_build_config,
"--",
"-d",
_class_dir,
"@" + rebase_path(_java_sources_file, root_build_dir),
] + _javac_flags
}

script = jar_runner
# Bundles all files within the 'class directory' into a jar file
action(target_name) {
deps = [ ":$_javac_target_name" ] + _deps

args = [
"cf",
rebase_path(_jar_output, root_build_dir),
"-C",
_class_dir,
".",
]
data_deps = _data_deps

outputs = [ _jar_output ]

script = jar_runner

args = [
"cf",
rebase_path(_jar_output, root_build_dir),
"-C",
_class_dir,
".",
]
}
} else {
# Using pre-specified JAR instead of building from sources - simply copy the JAR to the output directory.
_original_jar_path = invoker.jar_path
copy(target_name) {
sources = [ _original_jar_path ]
outputs = [ _jar_output ]
deps = [ ":$_config_target_name" ] + _deps
}
}
}

Expand All @@ -121,8 +185,6 @@ template("android_library") {
}

javac_flags += [
"-classpath",
"${android_sdk_root}/platforms/android-${android_sdk_version}/android.jar",
"-Xlint:-options",
"-source",
"8",
Expand All @@ -131,3 +193,9 @@ template("android_library") {
]
}
}

template("java_prebuilt") {
java_library(target_name) {
forward_variables_from(invoker, "*")
}
}
63 changes: 63 additions & 0 deletions build/chip/java/tests/BUILD.gn
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Copyright (c) 2021 Project CHIP Authors
#
# Licensed 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.

import("//build_overrides/chip.gni")
import("//build_overrides/pigweed.gni")
import("$dir_pw_build/python.gni")
import("${chip_root}/build/chip/java/rules.gni")

pw_python_script("java_build_test") {
inputs = [
"expected_output/child_library_2_expected.json",
"expected_output/grandchild_library_expected.json",
"expected_output/child_library_expected.json",
"expected_output/java_library_expected.json",
"expected_output/child_prebuilt_expected.json",
"expected_output/java_prebuilt_expected.json",
]
other_deps = [ ":java_library" ]
tests = [ "test.py" ]
}

java_library("java_library") {
sources = [ "IncludedInSources.java" ]
deps = [
":child_library",
":child_library_2",
":java_prebuilt",
]
}

java_library("child_library") {
sources = [ "IncludedInChildSources.java" ]
}

java_library("child_library_2") {
sources = [ "IncludedInChildSources2.java" ]

deps = [ ":grandchild_library" ]
}

java_library("grandchild_library") {
sources = [ "IncludedInGrandchildSources.java" ]
}

java_prebuilt("java_prebuilt") {
jar_path = "/tmp/chip_java_build_test/prebuilt_jar.jar"
deps = [ ":child_prebuilt" ]
}

java_prebuilt("child_prebuilt") {
jar_path = "/tmp/chip_java_build_test/child_jar.jar"
}
3 changes: 3 additions & 0 deletions build/chip/java/tests/IncludedInChildJar.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package build.chip.java.tests;

public class IncludedInChildJar {}
3 changes: 3 additions & 0 deletions build/chip/java/tests/IncludedInChildSources.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package build.chip.java.tests;

public class IncludedInChildSources {}
5 changes: 5 additions & 0 deletions build/chip/java/tests/IncludedInChildSources2.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package build.chip.java.tests;

public class IncludedInChildSources2 {
IncludedInGrandchildSources includedInGrandchildSources;
}
3 changes: 3 additions & 0 deletions build/chip/java/tests/IncludedInGrandchildSources.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package build.chip.java.tests;

public class IncludedInGrandchildSources {}
5 changes: 5 additions & 0 deletions build/chip/java/tests/IncludedInJar.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package build.chip.java.tests;

public class IncludedInJar {
IncludedInChildJar includedInChildJar;
}
9 changes: 9 additions & 0 deletions build/chip/java/tests/IncludedInSources.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package build.chip.java.tests;

public class IncludedInSources {
IncludedInChildSources includedInChildSources;
IncludedInChildSources2 includedInChildSources2;
IncludedInGrandchildSources includedInGrandchildSources;

IncludedInJar includedInJar;
}
Loading

0 comments on commit c9c3b65

Please sign in to comment.