Skip to content
This repository has been archived by the owner on Nov 10, 2023. It is now read-only.

Commit

Permalink
Implement a build_config generator from #95
Browse files Browse the repository at this point in the history
Summary:
Simplified the build steps a bit.
Use ProjectFilesystem in GenerateBuildConfigStep, and try-with-resources.
Use System.lineSeparator() for platform independent newlines.
Make field final.
Cleaned up unnecessary getter methods.
Simplified creating buildable/build rule in the unit test.
Fixed styling.

I will follow up with another diff that updates the soy docs.

Test Plan: buck test --all
  • Loading branch information
natthu authored and oconnor663 committed Apr 17, 2014
1 parent e72b64f commit 4c3b7f4
Show file tree
Hide file tree
Showing 6 changed files with 354 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/com/facebook/buck/android/BUCK
Expand Up @@ -31,6 +31,8 @@ RULES_SRCS = [
'AndroidTransitiveDependencyGraph.java',
'ApkGenrule.java',
'ApkGenruleDescription.java',
'BuildConfig.java',
'BuildConfigDescription.java',
'ComputeExopackageDepsAbi.java',
'DexProducedFromJavaLibrary.java',
'DexRDotJavaStep.java',
Expand Down
104 changes: 104 additions & 0 deletions src/com/facebook/buck/android/BuildConfig.java
@@ -0,0 +1,104 @@
/*
* Copyright 2014-present Facebook, Inc.
*
* 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.
*/

package com.facebook.buck.android;

import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.model.BuildTargets;
import com.facebook.buck.rules.AbstractBuildable;
import com.facebook.buck.rules.BuildContext;
import com.facebook.buck.rules.Buildable;
import com.facebook.buck.rules.BuildableContext;
import com.facebook.buck.rules.RuleKey;
import com.facebook.buck.step.Step;
import com.facebook.buck.step.fs.MakeCleanDirectoryStep;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;

import java.io.IOException;
import java.nio.file.Path;
import java.util.Collection;
import java.util.List;

/**
* {@link BuildConfig} is a {@link Buildable} that can generate a BuildConfig.java file.
* <pre>
* build_config(
* name = 'debug_build_config',
* package = 'com.example.package',
* debug = True,
* )
* </pre>
* This will produce a genfile that will be parameterized by the name of the
* {@code build_config} rule.
* This can be used as a dependency for example
* for an android library:
* <pre>
* android_library(
* name = 'my-app-lib',
* srcs = [':debug_build_config'] + glob(['src/**&#47;*.java']),
* deps = [
* ':debug_build_config',
* ],
* )
* </pre>
*/
public class BuildConfig extends AbstractBuildable {

private final String appPackage;
private final boolean debug;
private final Path pathToOutputFile;

protected BuildConfig(
BuildTarget buildTarget,
String appPackage,
boolean debug) {
this.appPackage = Preconditions.checkNotNull(appPackage);
this.debug = debug;
this.pathToOutputFile = BuildTargets.getGenPath(buildTarget, "__%s__")
.resolve("BuildConfig.java");
}

@Override
public Collection<Path> getInputsToCompareToOutput() {
return ImmutableList.of();
}

@Override
public RuleKey.Builder appendDetailsToRuleKey(RuleKey.Builder builder) {
return builder
.set("package", appPackage)
.set("debug", debug);
}

@Override
public List<Step> getBuildSteps(BuildContext context, BuildableContext buildableContext)
throws IOException {
ImmutableList.Builder<Step> steps = ImmutableList.builder();

steps.add(new MakeCleanDirectoryStep(pathToOutputFile.getParent()));
steps.add(new GenerateBuildConfigStep(appPackage, debug, pathToOutputFile));

buildableContext.recordArtifact(pathToOutputFile);
return steps.build();
}

@Override
public Path getPathToOutputFile() {
return pathToOutputFile;
}

}
49 changes: 49 additions & 0 deletions src/com/facebook/buck/android/BuildConfigDescription.java
@@ -0,0 +1,49 @@
/*
* Copyright 2014-present Facebook, Inc.
*
* 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.
*/

package com.facebook.buck.android;

import com.facebook.buck.rules.BuildRuleParams;
import com.facebook.buck.rules.BuildRuleType;
import com.facebook.buck.rules.ConstructorArg;
import com.facebook.buck.rules.Description;
import com.facebook.buck.rules.Hint;

public class BuildConfigDescription implements Description<BuildConfigDescription.Arg> {

public static final BuildRuleType TYPE = new BuildRuleType("build_config");

@Override
public BuildRuleType getBuildRuleType() {
return TYPE;
}

@Override
public Arg createUnpopulatedConstructorArg() {
return new Arg();
}

@Override
public BuildConfig createBuildable(BuildRuleParams params, Arg args) {
return new BuildConfig(params.getBuildTarget(), args.appPackage, args.debug);
}

public static class Arg implements ConstructorArg {
@Hint(name = "package")
public String appPackage;
public boolean debug;
}
}
100 changes: 100 additions & 0 deletions src/com/facebook/buck/android/GenerateBuildConfigStep.java
@@ -0,0 +1,100 @@
/*
* Copyright 2014-present Facebook, Inc.
*
* 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.
*/

package com.facebook.buck.android;

import com.facebook.buck.step.ExecutionContext;
import com.facebook.buck.step.Step;
import com.facebook.buck.util.HumanReadableException;
import com.facebook.buck.util.ProjectFilesystem;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;

import java.io.IOException;
import java.nio.file.Path;

public class GenerateBuildConfigStep implements Step {

private final String configPackage;
private final boolean debug;
private final Path outBuildConfigPath;

public GenerateBuildConfigStep(
String configPackage,
boolean debug,
Path outBuildConfigPath) {
this.configPackage = Preconditions.checkNotNull(configPackage);
this.debug = debug;
this.outBuildConfigPath = Preconditions.checkNotNull(outBuildConfigPath);
if (outBuildConfigPath.getNameCount() == 0) {
throw new HumanReadableException("Output BuildConfig.java filepath is missing");
}

}

@Override
public int execute(ExecutionContext context) {
StringBuilder builder = new StringBuilder();
builder.append("package ");
builder.append(configPackage);
builder.append(";\n");
builder.append("public final class BuildConfig {\n");
builder.append(" public final static boolean DEBUG = ");
builder.append(String.valueOf(debug));
builder.append(";\n");
builder.append("}");

ProjectFilesystem filesystem = context.getProjectFilesystem();
try {
filesystem.writeContentsToPath(builder.toString(), outBuildConfigPath);
} catch (IOException e) {
context.logError(e, "Error writing BuildConfig.java: %s", outBuildConfigPath);
return 1;
}

return 0;
}

@Override
public String getDescription(ExecutionContext context) {
return String.format("generate_build_config %s %s", configPackage, String.valueOf(debug));
}

@Override
public String getShortName() {
return "generate_build_config";
}

@Override
public boolean equals(Object obj) {
if (!(obj instanceof GenerateBuildConfigStep)) {
return false;
}

GenerateBuildConfigStep that = (GenerateBuildConfigStep) obj;
return Objects.equal(this.configPackage, that.configPackage) &&
Objects.equal(this.debug, that.debug) &&
Objects.equal(this.outBuildConfigPath, that.outBuildConfigPath);
}

@Override
public int hashCode() {
return Objects.hashCode(
configPackage,
debug,
outBuildConfigPath);
}
}
2 changes: 2 additions & 0 deletions src/com/facebook/buck/rules/KnownBuildRuleTypes.java
Expand Up @@ -22,6 +22,7 @@
import com.facebook.buck.android.AndroidManifestDescription;
import com.facebook.buck.android.AndroidResourceDescription;
import com.facebook.buck.android.ApkGenruleDescription;
import com.facebook.buck.android.BuildConfigDescription;
import com.facebook.buck.android.GenAidlDescription;
import com.facebook.buck.android.NdkLibraryDescription;
import com.facebook.buck.android.PrebuiltNativeLibraryDescription;
Expand Down Expand Up @@ -163,6 +164,7 @@ static Builder createBuilder(
builder.register(new AndroidManifestDescription());
builder.register(new AndroidResourceDescription());
builder.register(new ApkGenruleDescription());
builder.register(new BuildConfigDescription());
builder.register(new CppBinaryDescription());
builder.register(new CppLibraryDescription());
builder.register(new ExportFileDescription());
Expand Down
97 changes: 97 additions & 0 deletions test/com/facebook/buck/android/BuildConfigTest.java
@@ -0,0 +1,97 @@
/*
* Copyright 2014-present Facebook, Inc.
*
* 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.
*/

package com.facebook.buck.android;

import static org.junit.Assert.assertEquals;

import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.rules.AbstractBuildable;
import com.facebook.buck.rules.BuildContext;
import com.facebook.buck.rules.BuildRule;
import com.facebook.buck.rules.Buildable;
import com.facebook.buck.rules.FakeBuildRuleParams;
import com.facebook.buck.rules.FakeBuildableContext;
import com.facebook.buck.step.Step;
import com.facebook.buck.util.BuckConstant;

import org.easymock.EasyMock;
import org.junit.Test;

import java.io.IOException;
import java.util.List;

/**
* Unit test for {@link BuildConfig}.
*/
public class BuildConfigTest {

/**
* Tests the following methods:
* <ul>
* <li>{@link Buildable#getInputsToCompareToOutput()}
* <li>{@link BuildConfig#getPathToOutputFile()}
* </ul>
*/
@Test
public void testSimpleObserverMethods() {
BuildRule buildRule = createSimpleBuildConfigRule();
BuildConfig buildConfigRule = (BuildConfig) buildRule.getBuildable();

assertEquals(
BuckConstant.GEN_PATH.resolve("java/com/example/__build_config__/BuildConfig.java"),
buildConfigRule.getPathToOutputFile());
}

@Test
public void testBuildInternal() throws IOException {
BuildRule buildRule = createSimpleBuildConfigRule();
BuildConfig buildConfigRule = (BuildConfig) buildRule.getBuildable();

// Mock out a BuildContext whose DependencyGraph will be traversed.
BuildContext buildContext = EasyMock.createMock(BuildContext.class);
EasyMock.replay(buildContext);

List<Step> steps = buildConfigRule.getBuildSteps(buildContext, new FakeBuildableContext());
Step generateBuildConfigStep = steps.get(1);
GenerateBuildConfigStep expectedStep = new GenerateBuildConfigStep(
/* appPackage */ "com.example",
/* debug */ true,
BuckConstant.GEN_PATH.resolve("java/com/example/__build_config__/BuildConfig.java"));
assertEquals(expectedStep, generateBuildConfigStep);
}

@Test
public void testGetTypeMethodOfBuilder() {
assertEquals("build_config", BuildConfigDescription.TYPE.getName());
}

private static BuildRule createSimpleBuildConfigRule() {
// First, create the BuildConfig object.
BuildTarget buildTarget = new BuildTarget("//java/com/example", "build_config");
BuildConfig buildConfig = new BuildConfig(
buildTarget,
/* appPackage */ "com.example",
/* debug */ true);
return new AbstractBuildable.AnonymousBuildRule(
BuildConfigDescription.TYPE,
buildConfig,
new FakeBuildRuleParams(buildTarget));
}

// TODO(nickpalmer): Add another unit test that passes in a non-trivial DependencyGraph and verify
// that the resulting set of libraryManifestPaths is computed correctly.
}

1 comment on commit 4c3b7f4

@nickpalmer
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the //TODO note can be dropped actually. Left over from the cribbing I did from the AndroidManifest rule.

Please sign in to comment.