Skip to content
12 changes: 10 additions & 2 deletions source/AndroidResolver/src/GradleResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,15 @@ private void LogMissingDependenciesError(List<string> missingArtifacts) {
}
}

/// <summary>
/// Get package spec from a dependency.
/// </summary>
/// <param name="dependency">Dependency instance to query for package spec.</param>
internal static string DependencyToPackageSpec(Dependency dependency) {
return dependency.Version.ToUpper() == "LATEST" ?
dependency.VersionlessKey + ":+" : dependency.Key;
}

/// <summary>
/// From a list of dependencies generate a list of Maven / Gradle / Ivy package spec
/// strings.
Expand All @@ -116,8 +125,7 @@ internal static Dictionary<string, string> DependenciesToPackageSpecs(
var sourcesByPackageSpec = new Dictionary<string, string>();
foreach (var dependency in dependencies) {
// Convert the legacy "LATEST" version spec to a Gradle version spec.
var packageSpec = dependency.Version.ToUpper() == "LATEST" ?
dependency.VersionlessKey + ":+" : dependency.Key;
var packageSpec = DependencyToPackageSpec(dependency);
var source = CommandLine.SplitLines(dependency.CreatedBy)[0];
string sources;
if (sourcesByPackageSpec.TryGetValue(packageSpec, out sources)) {
Expand Down
49 changes: 45 additions & 4 deletions source/AndroidResolver/src/PlayServicesResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2191,15 +2191,56 @@ internal static IList<string> GradleDependenciesLines(
// https://docs.gradle.org/3.4/release-notes.html#the-java-library-plugin
// https://developer.android.com/studio/releases/gradle-plugin#3-0-0
var version = GradleVersion;
var versionComparer = new Dependency.VersionComparer();
var includeStatement =
!String.IsNullOrEmpty(version) &&
(new Dependency.VersionComparer()).Compare("3.4", version) >= 0 ?
versionComparer.Compare("3.4", version) >= 0 ?
"implementation" : "compile";
if (includeDependenciesBlock) lines.Add("dependencies {");
// Build a map of dependencies with the max version of that dependency.
// If different packages have different versions of the same dependency,
// we want to activate only the highest version but still include the
// other versions as commented out dependency lines.
var dependenciesMaxVersions = new Dictionary<string, Dependency>();
// Maintain a set of packages which we want to comment out.
HashSet<string> packageSpecStringsToComment = new HashSet<string>();
foreach( var dependency in dependencies) {
Dependency dependencyMaxVersion;
if (dependenciesMaxVersions.TryGetValue(dependency.VersionlessKey, out dependencyMaxVersion)){
if(versionComparer.Compare(dependency.Version, dependencyMaxVersion.Version) < 0) {
dependenciesMaxVersions[dependency.VersionlessKey] = dependency;
// We found a new larger version. Comment out older one
// Build a single item list since `GetPackageSpecs`
// accepts an IEnumerable type.
var packageSpecString = GradleResolver.DependencyToPackageSpec(dependencyMaxVersion);
packageSpecStringsToComment.Add(packageSpecString);
}
else {
// Dependency version is smaller than current max.
var packageSpecString = GradleResolver.DependencyToPackageSpec(dependency);
packageSpecStringsToComment.Add(packageSpecString);
}
}
else {
// First time encountering this dependency.
dependenciesMaxVersions[dependency.VersionlessKey] = dependency;
}
}
foreach (var packageSpecAndSources in GetPackageSpecs(dependencies: dependencies)) {
lines.Add(String.Format(
" {0} '{1}' // {2}", includeStatement, packageSpecAndSources.Key,
packageSpecAndSources.Value));
string line = " ";
if (packageSpecStringsToComment.Contains(packageSpecAndSources.Key)) {
PlayServicesResolver.Log(
String.Format(
"Ignoring duplicate package {0} with older version.",
packageSpecAndSources.Key),
level: LogLevel.Info
);
line += "// ";
}
line += String.Format(
"{0} '{1}' // {2}", includeStatement, packageSpecAndSources.Key,
packageSpecAndSources.Value);
lines.Add(line);
}
if (includeDependenciesBlock) lines.Add("}");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<dependencies>
<androidPackages>
<!-- Duplicate entry for package with a different version (higher). -->
<androidPackage spec="com.google.android.gms:play-services-base:18.0.1" />
<!-- Duplicate entry for package with a different version (lower). -->
<androidPackage spec="com.google.android.gms:play-services-base:12.0.1" />
<!-- Duplicate entry for package with a different version (wildcard). -->
<androidPackage spec="com.google.firebase:firebase-app-unity:5.+"/>
<!-- Duplicate entry for package with the exact same version. -->
<androidPackage spec="com.android.support:support-annotations:26.1.0"/>
</androidPackages>
</dependencies>

Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// GENERATED BY UNITY. REMOVE THIS COMMENT TO PREVENT OVERWRITING WHEN EXPORTING AGAIN

buildscript {
repositories {
jcenter()
google()
}

dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
}
}

allprojects {
repositories {
flatDir {
dirs 'libs'
}
}
}

// Android Resolver Repos Start
([rootProject] + (rootProject.subprojects as List)).each { project ->
project.repositories {
def unityProjectPath = $/file:///**DIR_UNITYPROJECT**/$.replace("\\", "/")
maven {
url "https://maven.google.com"
}
maven {
url "file:///my/nonexistant/test/repo" // Assets/ExternalDependencyManager/Editor/TestDependencies.xml:17
}
maven {
url (unityProjectPath + "/project_relative_path/repo") // Assets/ExternalDependencyManager/Editor/TestDependencies.xml:17
}
maven {
url (unityProjectPath + "/Assets/GeneratedLocalRepo/Firebase/m2repository") // Assets/ExternalDependencyManager/Editor/TestDependencies.xml:10
}
mavenLocal()
jcenter()
mavenCentral()
}
}
// Android Resolver Repos End
apply plugin: 'com.android.application'

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
// Android Resolver Dependencies Start
compile 'com.android.support:support-annotations:26.1.0' // Assets/ExternalDependencyManager/Editor/TestDependencies.xml:4
// compile 'com.google.android.gms:play-services-base:12.0.1' // Assets/ExternalDependencyManager/Editor/TestAdditionalDuplicateDependencies.xml:6
// compile 'com.google.android.gms:play-services-base:15.0.1' // Assets/ExternalDependencyManager/Editor/TestAdditionalDependencies.xml:3
compile 'com.google.android.gms:play-services-base:18.0.1' // Assets/ExternalDependencyManager/Editor/TestAdditionalDuplicateDependencies.xml:4
// compile 'com.google.firebase:firebase-app-unity:5.+' // Assets/ExternalDependencyManager/Editor/TestAdditionalDuplicateDependencies.xml:8
compile 'com.google.firebase:firebase-app-unity:5.1.1' // Assets/ExternalDependencyManager/Editor/TestDependencies.xml:10
compile 'com.google.firebase:firebase-common:16.0.0' // Google.AndroidResolverIntegrationTests.SetupDependencies
compile 'org.test.psr:classifier:1.0.1:foo@aar' // Assets/ExternalDependencyManager/Editor/TestDependencies.xml:12
// Android Resolver Dependencies End
**DEPS**}

// Android Resolver Exclusions Start
android {
packagingOptions {
exclude ('lib/unsupported/libFirebaseCppApp-5.1.1.so')
}
}
// Android Resolver Exclusions End
android {
compileSdkVersion **APIVERSION**
buildToolsVersion '**BUILDTOOLS**'

defaultConfig {
minSdkVersion **MINSDKVERSION**
targetSdkVersion **TARGETSDKVERSION**
applicationId '**APPLICATIONID**'
ndk {
abiFilters **ABIFILTERS**
}
versionCode **VERSIONCODE**
versionName '**VERSIONNAME**'
}

lintOptions {
abortOnError false
}

aaptOptions {
noCompress '.unity3d', '.ress', '.resource', '.obb'**STREAMING_ASSETS**
}**SIGN**

buildTypes {
debug {
minifyEnabled **MINIFY_DEBUG**
useProguard **PROGUARD_DEBUG**
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-unity.txt'**USER_PROGUARD**
jniDebuggable true
}
release {
minifyEnabled **MINIFY_RELEASE**
useProguard **PROGUARD_RELEASE**
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-unity.txt'**USER_PROGUARD****SIGNCONFIG**
}
}**PACKAGING_OPTIONS****SPLITS**
**BUILT_APK_LOCATION**
}**SPLITS_VERSION_CODE****SOURCE_BUILD_SETUP**
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// GENERATED BY UNITY. REMOVE THIS COMMENT TO PREVENT OVERWRITING WHEN EXPORTING AGAIN

buildscript {
repositories {
jcenter()
google()
}

dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
}
}

allprojects {
repositories {
flatDir {
dirs 'libs'
}
}
}

apply plugin: 'com.android.application'

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
**DEPS**}

android {
compileSdkVersion **APIVERSION**
buildToolsVersion '**BUILDTOOLS**'

defaultConfig {
minSdkVersion **MINSDKVERSION**
targetSdkVersion **TARGETSDKVERSION**
applicationId '**APPLICATIONID**'
ndk {
abiFilters **ABIFILTERS**
}
versionCode **VERSIONCODE**
versionName '**VERSIONNAME**'
}

lintOptions {
abortOnError false
}

aaptOptions {
noCompress '.unity3d', '.ress', '.resource', '.obb'**STREAMING_ASSETS**
}**SIGN**

buildTypes {
debug {
minifyEnabled **MINIFY_DEBUG**
useProguard **PROGUARD_DEBUG**
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-unity.txt'**USER_PROGUARD**
jniDebuggable true
}
release {
minifyEnabled **MINIFY_RELEASE**
useProguard **PROGUARD_RELEASE**
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-unity.txt'**USER_PROGUARD****SIGNCONFIG**
}
}**PACKAGING_OPTIONS****SPLITS**
**BUILT_APK_LOCATION**
}**SPLITS_VERSION_CODE****SOURCE_BUILD_SETUP**
42 changes: 38 additions & 4 deletions source/AndroidResolver/test/src/AndroidResolverIntegrationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ public class AndroidResolverIntegrationTests {
/// </summary>
private const string ADDITIONAL_DEPENDENCIES_FILENAME = "TestAdditionalDependencies";

/// <summary>
/// The name of the file, without extension, that will serve as a template for dynamically
/// adding additional dependencies with a duplicate package with a different version.
/// </summary>
private const string ADDITIONAL_DUPLICATE_DEPENDENCIES_FILENAME = "TestAdditionalDuplicateDependencies";

/// <summary>
/// Disabled application Gradle template file.
/// </summary>
Expand Down Expand Up @@ -129,6 +135,29 @@ public static void ConfigureTestCases() {
});
}
},
new IntegrationTester.TestCase {
Name = "ResolveForGradleBuildSystemWithDuplicatePackages",
Method = (testCase, testCaseComplete) => {
ClearAllDependencies();
SetupDependencies();
// Add 2 additional dependency files (each file contains a single package
// but with different versions).
UpdateAdditionalDependenciesFile(true, ADDITIONAL_DEPENDENCIES_FILENAME);
UpdateAdditionalDependenciesFile(true, ADDITIONAL_DUPLICATE_DEPENDENCIES_FILENAME);

ResolveWithGradleTemplate(
GRADLE_TEMPLATE_DISABLED,
"ExpectedArtifacts/NoExport/GradleTemplateDuplicatePackages",
testCase, testCaseComplete,
otherExpectedFiles: new [] {
"Assets/GeneratedLocalRepo/Firebase/m2repository/com/google/" +
"firebase/firebase-app-unity/5.1.1/firebase-app-unity-5.1.1.aar" },
filesToIgnore: new HashSet<string> {
Path.GetFileName(GRADLE_TEMPLATE_LIBRARY_DISABLED),
Path.GetFileName(GRADLE_TEMPLATE_PROPERTIES_DISABLED)
});
}
},
new IntegrationTester.TestCase {
Name = "ResolverForGradleBuildSystemWithTemplateUsingJetifier",
Method = (testCase, testCaseComplete) => {
Expand Down Expand Up @@ -474,7 +503,8 @@ private static void ClearAllDependencies() {
GooglePlayServices.SettingsDialog.PatchPropertiesTemplateGradle = false;

PlayServicesSupport.ResetDependencies();
UpdateAdditionalDependenciesFile(false);
UpdateAdditionalDependenciesFile(false, ADDITIONAL_DEPENDENCIES_FILENAME);
UpdateAdditionalDependenciesFile(false, ADDITIONAL_DUPLICATE_DEPENDENCIES_FILENAME);
}

/// <summary>
Expand Down Expand Up @@ -565,14 +595,18 @@ private static void CompareKeyValuePairLists(
/// </summary>
/// <param name="addDependencyFile">If true, will copy the template file to an XML file if it
/// doesn't exist. If false, delete the XML file if it exists.</param>
private static void UpdateAdditionalDependenciesFile(bool addDependencyFile) {
/// <param name="filename">Name of the template file (without extension) to
/// create an XML from. </param>
private static void UpdateAdditionalDependenciesFile(
bool addDependencyFile,
string filename=ADDITIONAL_DEPENDENCIES_FILENAME) {
string currentDirectory = Directory.GetCurrentDirectory();
string editorPath = Path.Combine(currentDirectory,
"Assets/ExternalDependencyManager/Editor/");

string templateFilePath = Path.Combine(editorPath, ADDITIONAL_DEPENDENCIES_FILENAME +
string templateFilePath = Path.Combine(editorPath, filename+
".template");
string xmlFilePath = Path.Combine(editorPath, ADDITIONAL_DEPENDENCIES_FILENAME + ".xml");
string xmlFilePath = Path.Combine(editorPath, filename+ ".xml");
if (addDependencyFile && !File.Exists(xmlFilePath)) {
if (!File.Exists(templateFilePath)) {
UnityEngine.Debug.LogError("Could not find file: " + templateFilePath);
Expand Down