Skip to content

add gradle tasks for metalava generation #671

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

Merged
merged 15 commits into from
Aug 14, 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
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,27 @@
package com.google.firebase.gradle.plugins;

import com.android.build.gradle.LibraryExtension;
import com.android.build.gradle.api.AndroidSourceSet;
import com.android.build.gradle.api.LibraryVariant;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.firebase.gradle.plugins.apiinfo.GenerateApiTxtFileTask;
import com.google.firebase.gradle.plugins.apiinfo.ApiInformationTask;
import com.google.firebase.gradle.plugins.ci.device.FirebaseTestServer;

import java.util.Collection;
import org.codehaus.groovy.util.ReleaseInfo;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.attributes.Attribute;
import org.gradle.api.file.FileCollection;
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile;
import java.io.File;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class FirebaseLibraryPlugin implements Plugin<Project> {

Expand Down Expand Up @@ -54,6 +69,10 @@ public void apply(Project project) {
}
});
}
if (System.getenv().containsKey("METALAVA_BINARY_PATH")) {
setupApiInfomrationAnalysis(project, android);
}


android.testServer(new FirebaseTestServer(project, firebaseLibrary.testLab));

Expand All @@ -71,6 +90,49 @@ public void apply(Project project) {
ImmutableList.of("-module-name", kotlinModuleName(project))));
}

private static void setupApiInfomrationAnalysis(Project project, LibraryExtension android) {

String metalavaBinaryPath = System.getenv("METALAVA_BINARY_PATH");
AndroidSourceSet mainSourceSet = android.getSourceSets().getByName("main");
File outputFile = project.getRootProject().file(Paths.get(
project.getRootProject().getBuildDir().getPath(),
"apiinfo",
project.getPath().substring(1).replace(":", "_")));
File outputApiFile = new File(outputFile.getAbsolutePath() + "_api.txt");
String sourcePathArgument = mainSourceSet.getJava().getSrcDirs().stream()
.map(File::getAbsolutePath)
.collect(Collectors.joining(":"));
if(mainSourceSet.getJava().getSrcDirs().stream().noneMatch(File::exists)) {
return;
}

project.getTasks().register("apiInformation", ApiInformationTask.class, task -> {
task.setApiTxt(project.file("api.txt"));
task.setMetalavaBinaryPath(metalavaBinaryPath);
task.setSourcePath(sourcePathArgument);
task.setOutputFile(outputFile);
task.setBaselineFile(project.file("baseline.txt"));
task.setOutputApiFile(outputApiFile);
if (project.hasProperty("updateBaseline")) {
task.setUpdateBaseline(true);
} else {
task.setUpdateBaseline(false);
}
});

project.getTasks().register("generateApiTxtFile", GenerateApiTxtFileTask.class, task -> {
task.setApiTxt(project.file("api.txt"));
task.setMetalavaBinaryPath(metalavaBinaryPath);
task.setSourcePath(sourcePathArgument);
task.setBaselineFile(project.file("baseline.txt"));
if (project.hasProperty("updateBaseline")) {
task.setUpdateBaseline(true);
} else {
task.setUpdateBaseline(false);
}
});
}

private static void setupStaticAnalysis(
Project project, LibraryExtension android, FirebaseLibraryExtension library) {
project.afterEvaluate(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// Copyright 2019 Google LLC
//
// 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.google.firebase.gradle.plugins.apiinfo;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.gradle.api.DefaultTask;
import org.gradle.api.GradleException;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.OutputFile;
import org.gradle.api.tasks.TaskAction;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.Arrays;

/**
Task generates the api diff of the current source code against the api.txt file stored
alongside the project's src directory.
*/
public abstract class ApiInformationTask extends DefaultTask {

@Input
abstract String getMetalavaBinaryPath();

@InputFile
abstract File getApiTxt();

@Input
abstract String getSourcePath();

@OutputFile
abstract File getBaselineFile();

@OutputFile
abstract File getOutputApiFile();

@Input
abstract boolean getUpdateBaseline();

@OutputFile
abstract File getOutputFile();

public abstract void setSourcePath(String value);

public abstract void setBaselineFile(File value);

public abstract void setUpdateBaseline(boolean value);

public abstract void setMetalavaBinaryPath(String value);

public abstract void setApiTxt(File value);

public abstract void setOutputApiFile(File value);

public abstract void setOutputFile(File value);


@TaskAction
void execute() {
File outputFileDir = getOutputFile().getParentFile();
if(!outputFileDir.exists()) {
outputFileDir.mkdirs();
}

// Generate api.txt file and store it in the build directory.
getProject().exec(spec-> {
spec.setCommandLine(Arrays.asList(
getMetalavaBinaryPath(),
"--source-path", getSourcePath(),
"--api", getOutputApiFile().getAbsolutePath(),
"--format=v2"
));
spec.setIgnoreExitValue(true);
});
getProject().exec(spec-> {
List<String> cmd = new ArrayList<>(Arrays.asList(
getMetalavaBinaryPath(),
"--source-files", getOutputApiFile().getAbsolutePath(),
"--check-compatibility:api:current", getApiTxt().getAbsolutePath(),
"--format=v2",
"--no-color",
"--delete-empty-baselines"
));
if(getUpdateBaseline()) {
cmd.addAll(Arrays.asList("--update-baseline", getBaselineFile().getAbsolutePath()));
} else if(getBaselineFile().exists()) {
cmd.addAll(Arrays.asList("--baseline", getBaselineFile().getAbsolutePath()));
}
spec.setCommandLine(cmd);
spec.setIgnoreExitValue(true);
try {
spec.setStandardOutput(new FileOutputStream(getOutputFile()));
} catch (FileNotFoundException e) {
throw new GradleException("Unable to run the command", e);
}
});

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright 2019 Google LLC
//
// 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.google.firebase.gradle.plugins.apiinfo;


import java.util.ArrayList;
import java.util.List;
import org.gradle.api.DefaultTask;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.OutputFile;
import org.gradle.api.tasks.TaskAction;
import java.io.File;

import java.util.Arrays;


public abstract class GenerateApiTxtFileTask extends DefaultTask {

@Input
abstract String getMetalavaBinaryPath();

@OutputFile
abstract File getApiTxt();

@Input
abstract String getSourcePath();


@OutputFile
abstract File getBaselineFile();

@Input
abstract boolean getUpdateBaseline();


public abstract void setSourcePath(String value);

public abstract void setBaselineFile(File value);

public abstract void setUpdateBaseline(boolean value);

public abstract void setMetalavaBinaryPath(String value);

public abstract void setApiTxt(File value);

@TaskAction
void execute() {
List<String> cmd = new ArrayList<String>(Arrays.asList(
getMetalavaBinaryPath(),
"--source-path", getSourcePath(),
"--api", getApiTxt().getAbsolutePath(),
"--format=v2",
"--delete-empty-baselines"
));

if(getUpdateBaseline()) {
cmd.addAll(Arrays.asList("--update-baseline", getBaselineFile().getAbsolutePath()));
} else if(getBaselineFile().exists()) {
cmd.addAll(Arrays.asList("--baseline", getBaselineFile().getAbsolutePath()));
}

getProject().exec(spec -> {
spec.setCommandLine(cmd);
spec.setIgnoreExitValue(true);
});

}
}
37 changes: 37 additions & 0 deletions ci/fireci/fireci/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
import click
import os

from github import Github

from . import gradle
from . import ci_command
from . import stats
Expand Down Expand Up @@ -61,3 +63,38 @@ def smoke_tests(app_build_variant, test_apps_dir):
os.path.join(cwd, 'build', 'm2repository')),
workdir=location,
)


@click.option('--issue_number', 'issue_number', required=True)
@click.option('--repo_name', 'repo_name', required=True)
@click.option('--auth_token', 'auth_token', required=True)
@ci_command()
def api_information(auth_token, repo_name, issue_number):
"""Comments the api information on the pr"""

gradle.run('apiInformation')
dir_suffix = 'build/apiinfo'
comment_string = ""
for filename in os.listdir(dir_suffix):
subproject = filename
formatted_output_lines = []
with open(os.path.join(dir_suffix, filename), 'r') as f:
outputlines = f.readlines()
for line in outputlines:
if 'error' in line:
formatted_output_lines.append(line[line.find('error:'):])
elif 'warning' in line:
formatted_output_lines.append(line[line.find('warning:'):])

if formatted_output_lines:
comment_string += 'The public api surface has changed for the subproject {}:\n'.format(subproject)
comment_string += ''.join(formatted_output_lines)
comment_string += '\n\n'
if comment_string:
comment_string += ('Please update the api.txt files for the subprojects being affected by this change '
'with the files present in the artifacts directory. Also perform a major/minor bump accordingly.\n')
# Comment to github.
github_client = Github(auth_token)
repo = github_client.get_repo(repo_name)
pr = repo.get_pull(int(issue_number))
pr.create_issue_comment(comment_string)
1 change: 1 addition & 0 deletions ci/fireci/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
'click==7.0',
'opencensus==0.2.0',
'google-cloud-monitoring==0.31.1',
'PyGithub==1.43.8'
],
packages=find_packages(exclude=['tests']),
entry_points={
Expand Down
1 change: 1 addition & 0 deletions firebase-abt/api.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// Signature format: 2.0
Loading