Skip to content

Commit

Permalink
Merge pull request #173 from google/oss-licenses-issue-172
Browse files Browse the repository at this point in the history
Add oss-licenses-plugin support for dependency cycles in projects
  • Loading branch information
caller9 committed Apr 6, 2021
2 parents ab53116 + 3435384 commit c9ed0e4
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 15 deletions.
2 changes: 1 addition & 1 deletion oss-licenses-plugin/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ dependencies {
}

group = 'com.google.android.gms'
version = '0.10.3'
version = '0.10.4'

apply plugin: 'maven'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class DependencyTask extends DefaultTask {

private static final logger = LoggerFactory.getLogger(DependencyTask.class)

private ConfigurationContainer configurations
private Project project

@OutputDirectory
File outputDir
Expand All @@ -77,11 +77,15 @@ class DependencyTask extends DefaultTask {
*/
@Input
List<String> getDirectDependencies() {
return collectDependenciesFromConfigurations(configurations)
return collectDependenciesFromConfigurations(
project.getConfigurations(),
[project] as Set<Project>
)
}

protected List<String> collectDependenciesFromConfigurations(
ConfigurationContainer configurationContainer
ConfigurationContainer configurationContainer,
Set<Project> visitedProjects
) {
Set<String> directDependencies = new HashSet<>()
Set<Project> libraryProjects = new HashSet<>()
Expand All @@ -99,9 +103,15 @@ class DependencyTask extends DefaultTask {
}
}
for (Project libraryProject in libraryProjects) {
if (libraryProject in visitedProjects) {
continue
}
visitedProjects.add(libraryProject)
logger.info("Visiting dependency ${libraryProject.displayName}")
directDependencies.addAll(
collectDependenciesFromConfigurations(
libraryProject.getConfigurations()
libraryProject.getConfigurations(),
visitedProjects
)
)
}
Expand Down Expand Up @@ -151,7 +161,7 @@ class DependencyTask extends DefaultTask {
}

protected void updateDependencyArtifacts() {
for (Configuration configuration : configurations) {
for (Configuration configuration : project.getConfigurations()) {
Set<ResolvedArtifact> artifacts = getResolvedArtifacts(
configuration)
if (artifacts == null) {
Expand Down Expand Up @@ -287,7 +297,7 @@ class DependencyTask extends DefaultTask {
}
}

void setConfigurations(configurations) {
this.configurations = configurations
void setProject(Project project) {
this.project = project
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class OssLicensesPlugin implements Plugin<Project> {
def dependencyOutput = new File(project.buildDir,
"generated/third_party_licenses")
def generatedJson = new File(dependencyOutput, "dependencies.json")
getDependencies.setConfigurations(project.getConfigurations())
getDependencies.setProject(project)
getDependencies.outputDir = dependencyOutput
getDependencies.outputFile = generatedJson

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public void collectDependenciesFromConfigurations_multipleConfigs_combined() {
Collections.sort(allExpectedIds);

List<String> dependencies = dependencyTask
.collectDependenciesFromConfigurations(configurationContainer);
.collectDependenciesFromConfigurations(configurationContainer, new HashSet<>());
Collections.sort(dependencies);

verify(compileConfiguration, times(1)).getAllDependencies();
Expand Down Expand Up @@ -142,14 +142,107 @@ public void collectDependenciesFromConfigurations_libraryProject_combined() {
Collections.sort(allExpectedIds);

List<String> dependencies = dependencyTask
.collectDependenciesFromConfigurations(configurationContainer);
.collectDependenciesFromConfigurations(configurationContainer, new HashSet<>());
Collections.sort(dependencies);

verify(compileConfiguration, times(1)).getAllDependencies();
verify(libraryConfiguration, times(1)).getAllDependencies();
assertThat(dependencies, is(allExpectedIds));
}

@Test
public void collectDependenciesFromConfigurations_libraryDependencyCycle_visitedOnce() {
List<String> libraryProjectExpectedIds = Arrays.asList(
"com.example:api-thing:1.2.3",
"com.example:idk-rpc:4.5.6"
);
Project libraryProject = mock(Project.class);
ProjectDependency libraryProjectDependency = mockDependency(
ProjectDependency.class, "this:is:ignored");
when(libraryProjectDependency.getDependencyProject()).thenReturn(libraryProject);
// Library project self-dependency cycle by including itself as a dependent project.
DependencySet libraryDependencySet = mockDependencySet(new HashSet<Dependency>() {{
add(libraryProjectDependency);
add(mockDependency(ExternalModuleDependency.class, libraryProjectExpectedIds.get(0)));
add(mockDependency(ExternalModuleDependency.class, libraryProjectExpectedIds.get(1)));
}});
Configuration libraryConfiguration = mockConfiguration(
"compile",
/* canBeResolved = */ true,
libraryDependencySet);
ConfigurationContainer libraryConfigurationContainer = mock(ConfigurationContainer.class);
when(libraryConfigurationContainer.iterator())
.thenAnswer(ignored -> Collections.singletonList(libraryConfiguration).iterator());
when(libraryProject.getConfigurations()).thenReturn(libraryConfigurationContainer);

List<String> compileExpectedIds = Arrays.asList(
"com.example:real-dependency:1.2.3",
"com.example:totally-useful:4.5.6"
);
DependencySet dependencySet = mockDependencySet(new HashSet<Dependency>() {{
add(libraryProjectDependency);
add(mockDependency(ExternalModuleDependency.class, compileExpectedIds.get(0)));
add(mockDependency(ExternalModuleDependency.class, compileExpectedIds.get(1)));
}});
Configuration compileConfiguration = mockConfiguration(
"compile",
/* canBeResolved = */ true,
dependencySet);
ConfigurationContainer configurationContainer = mock(ConfigurationContainer.class);
when(configurationContainer.iterator())
.thenReturn(Collections.singletonList(compileConfiguration).iterator());
List<String> allExpectedIds = new ArrayList<>(compileExpectedIds);
allExpectedIds.addAll(libraryProjectExpectedIds);
Collections.sort(allExpectedIds);

List<String> dependencies = dependencyTask
.collectDependenciesFromConfigurations(configurationContainer, new HashSet<>());
Collections.sort(dependencies);

verify(compileConfiguration, times(1)).getAllDependencies();
verify(libraryConfiguration, times(1)).getAllDependencies();
verify(libraryProject, times(1)).getConfigurations();
assertThat(dependencies, is(allExpectedIds));
}

@Test
public void collectDependenciesFromConfigurations_appSelfCycle_visitedOnce() {
List<String> appExpectedIds = Arrays.asList(
"com.example:real-dependency:1.2.3",
"com.example:totally-useful:4.5.6"
);
Project appProject = mock(Project.class);
when(appProject.getDisplayName()).thenReturn("YOLO");
ProjectDependency appProjectDependency = mockDependency(
ProjectDependency.class, "this:is:ignored");
when(appProjectDependency.getDependencyProject()).thenReturn(appProject);
// App project self-dependency cycle by including itself as a dependent project.
DependencySet dependencySet = mockDependencySet(new HashSet<Dependency>() {{
add(appProjectDependency);
add(mockDependency(ExternalModuleDependency.class, appExpectedIds.get(0)));
add(mockDependency(ExternalModuleDependency.class, appExpectedIds.get(1)));
}});
Configuration compileConfiguration = mockConfiguration(
"compile",
/* canBeResolved = */ true,
dependencySet);
ConfigurationContainer configurationContainer = mock(ConfigurationContainer.class);
when(configurationContainer.iterator())
.thenAnswer(ignored -> Collections.singletonList(compileConfiguration).iterator());
when(appProject.getConfigurations()).thenReturn(configurationContainer);
Collections.sort(appExpectedIds);

List<String> dependencies = dependencyTask.collectDependenciesFromConfigurations(
configurationContainer,
Collections.singleton(appProject)
);
Collections.sort(dependencies);

verify(compileConfiguration, times(1)).getAllDependencies();
verify(appProject, never()).getConfigurations();
assertThat(dependencies, is(appExpectedIds));
}

@Test
public void collectDependenciesFromConfigurations_withTestConfig_ignored() {
List<String> expectedIds = Arrays.asList(
Expand All @@ -173,7 +266,7 @@ public void collectDependenciesFromConfigurations_withTestConfig_ignored() {
.thenReturn(Arrays.asList(compileConfiguration, testConfiguration).iterator());

List<String> dependencies = dependencyTask
.collectDependenciesFromConfigurations(configurationContainer);
.collectDependenciesFromConfigurations(configurationContainer, new HashSet<>());
Collections.sort(dependencies);

verify(compileConfiguration, times(1)).getAllDependencies();
Expand Down Expand Up @@ -204,7 +297,7 @@ public void collectDependenciesFromConfigurations_withUnresolvableConfig_ignored
.thenReturn(Arrays.asList(compileConfiguration, unresolvableConfiguration).iterator());

List<String> dependencies = dependencyTask
.collectDependenciesFromConfigurations(configurationContainer);
.collectDependenciesFromConfigurations(configurationContainer, new HashSet<>());
Collections.sort(dependencies);

verify(compileConfiguration, times(1)).getAllDependencies();
Expand All @@ -228,7 +321,7 @@ public void collectDependenciesFromConfigurations_unusableDependencyClass_ignore
.thenReturn(Collections.singletonList(configuration).iterator());

List<String> dependencies = dependencyTask
.collectDependenciesFromConfigurations(configurationContainer);
.collectDependenciesFromConfigurations(configurationContainer, new HashSet<>());
Collections.sort(dependencies);

verify(configuration, times(1)).getAllDependencies();
Expand Down Expand Up @@ -260,7 +353,7 @@ private DependencySet mockDependencySet(List<String> mavenIds) {

private DependencySet mockDependencySet(Set<Dependency> dependencies) {
DependencySet dependencySet = mock(DependencySet.class);
when(dependencySet.iterator()).thenReturn(dependencies.iterator());
when(dependencySet.iterator()).thenAnswer(ignored -> dependencies.iterator());
return dependencySet;
}

Expand Down

0 comments on commit c9ed0e4

Please sign in to comment.