Skip to content

Commit

Permalink
Merge pull request #172 from mezpahlan/windows_support
Browse files Browse the repository at this point in the history
Experimental Windows support - take 2
  • Loading branch information
joshafeinberg authored Nov 2, 2022
2 parents c5143b2 + b2acbc4 commit d205199
Show file tree
Hide file tree
Showing 10 changed files with 184 additions and 80 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.dropbox.affectedmoduledetector

import com.dropbox.affectedmoduledetector.util.toOsSpecificPath
import java.io.File

class AffectedModuleConfiguration {
Expand Down Expand Up @@ -41,7 +42,7 @@ class AffectedModuleConfiguration {
* @see CustomTask - Implementation class
* @see AffectedModuleDetectorPlugin - gradle plugin
*/
var customTasks = emptySet<AffectedModuleConfiguration.CustomTask>()
var customTasks = emptySet<CustomTask>()

/**
* Folder to place the log in
Expand All @@ -66,7 +67,8 @@ class AffectedModuleConfiguration {
requireNotNull(baseDir) {
"baseDir must be set to use pathsAffectingAllModules"
}
field = value
// Protect against users specifying the wrong path separator for their OS.
field = value.map { it.toOsSpecificPath() }.toSet()
}
get() {
field.forEach { path ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import com.dropbox.affectedmoduledetector.AffectedModuleDetector.Companion.DEPEN
import com.dropbox.affectedmoduledetector.AffectedModuleDetector.Companion.ENABLE_ARG
import com.dropbox.affectedmoduledetector.AffectedModuleDetector.Companion.MODULES_ARG
import com.dropbox.affectedmoduledetector.commitshaproviders.CommitShaProvider
import com.dropbox.affectedmoduledetector.util.toPathSections
import org.gradle.api.GradleException
import org.gradle.api.Project
import org.gradle.api.Task
Expand Down Expand Up @@ -495,9 +496,13 @@ class AffectedModuleDetectorImpl constructor(
}
}

private fun affectsAllModules(file: String): Boolean {
private fun affectsAllModules(relativeFilePath: String): Boolean {
logger?.info("Paths affecting all modules: ${config.pathsAffectingAllModules}")
return config.pathsAffectingAllModules.any { file.startsWith(it) }

val pathSections = relativeFilePath.toPathSections(rootProject.projectDir, git.getGitRoot())
val projectRelativePath = pathSections.joinToString(File.separatorChar.toString())

return config.pathsAffectingAllModules.any { projectRelativePath.startsWith(it) }
}

private fun findContainingProject(filePath: String): Project? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,18 @@
package com.dropbox.affectedmoduledetector

import com.dropbox.affectedmoduledetector.commitshaproviders.CommitShaProvider
import com.dropbox.affectedmoduledetector.util.toOsSpecificLineEnding
import com.dropbox.affectedmoduledetector.util.toOsSpecificPath
import org.gradle.api.logging.Logger
import java.io.File
import java.util.concurrent.TimeUnit
import org.gradle.api.logging.Logger

interface GitClient {
fun findChangedFiles(
top: Sha = "HEAD",
includeUncommitted: Boolean = false
): List<String>

fun getGitRoot(): File

/**
Expand All @@ -40,10 +43,12 @@ interface GitClient {
* Executes the given shell command and returns the stdout as a string.
*/
fun execute(command: String): String

/**
* Executes the given shell command and returns the stdout by lines.
*/
fun executeAndParse(command: String): List<String>

/**
* Executes the given shell command and returns the first stdout line.
*/
Expand Down Expand Up @@ -80,11 +85,13 @@ internal class GitClientImpl(
val sha = commitShaProvider.get(commandRunner)

// use this if we don't want local changes
return commandRunner.executeAndParse(if (includeUncommitted) {
"$CHANGED_FILES_CMD_PREFIX $sha"
} else {
"$CHANGED_FILES_CMD_PREFIX $top..$sha"
})
return commandRunner.executeAndParse(
if (includeUncommitted) {
"$CHANGED_FILES_CMD_PREFIX $sha"
} else {
"$CHANGED_FILES_CMD_PREFIX $top..$sha"
}
)
}

private fun findGitDirInParentFilepath(filepath: File): File? {
Expand All @@ -97,6 +104,7 @@ internal class GitClientImpl(
}
return null
}

@Suppress("LongParameterList")
private fun parseCommitLogString(
commitLogString: String,
Expand Down Expand Up @@ -162,13 +170,12 @@ internal class GitClientImpl(
check(proc.exitValue() == 0) { "Nonzero exit value running git command." }
return stdout
}

override fun executeAndParse(command: String): List<String> {
val response = execute(command)
return execute(command).toOsSpecificLineEnding()
.split(System.lineSeparator())
.filterNot {
it.isEmpty()
}
return response
.map { it.toOsSpecificPath() }
.filterNot { it.isEmpty() }
}

override fun executeAndParseFirst(command: String): String {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,31 @@
/*
* Copyright 2018 The Android Open Source Project
*
* 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.
*/
/*
* Copyright 2018 The Android Open Source Project
*
* 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.
*/

/*
* Copyright (c) 2020, Dropbox, Inc. All rights reserved.
*/
/*
* Copyright (c) 2020, Dropbox, Inc. All rights reserved.
*/

package com.dropbox.affectedmoduledetector

import com.dropbox.affectedmoduledetector.util.toPathSections
import org.gradle.api.Project
import org.gradle.api.logging.Logger
import java.io.File

/**
/**
* Creates a project graph for fast lookup by file path
*/
internal class ProjectGraph(project: Project, val gitRoot: File, val logger: Logger? = null) {
Expand Down Expand Up @@ -52,24 +53,11 @@ internal class ProjectGraph(project: Project, val gitRoot: File, val logger: Log
* Finds the project that contains the given file.
* The file's path prefix should match the project's path.
*/
fun findContainingProject(filePath: String): Project? {
val sections = filePath.split(File.separatorChar)
val realSections = sections.toMutableList()
val projectRelativeDir = findProjectRelativeDir()
for (dir in projectRelativeDir) {
if (realSections.isNotEmpty() && dir == realSections.first()) {
realSections.removeAt(0)
} else {
break
}
}

logger?.info("finding containing project for $filePath , sections: $realSections")
return rootNode.find(realSections, 0)
}
fun findContainingProject(relativeFilePath: String): Project? {
val pathSections = relativeFilePath.toPathSections(rootProjectDir, gitRoot)

private fun findProjectRelativeDir(): List<String> {
return rootProjectDir.toRelativeString(gitRoot).split(File.separatorChar)
logger?.info("finding containing project for $relativeFilePath , sections: $pathSections")
return rootNode.find(pathSections, 0)
}

private class Node(val logger: Logger? = null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.dropbox.affectedmoduledetector.util

import java.io.File

/**
* Converts a [String] representation of a relative [File] path to sections based on the OS
* specific separator character.
*/
fun String.toPathSections(rootProjectDir: File, gitRootDir: File): List<String> {
val realSections = toOsSpecificPath()
.split(File.separatorChar)
.toMutableList()
val projectRelativeDirectorySections = rootProjectDir
.toRelativeString(gitRootDir)
.split(File.separatorChar)
for (directorySection in projectRelativeDirectorySections) {
if (realSections.isNotEmpty() && realSections.first() == directorySection) {
realSections.removeAt(0)
} else {
break
}
}
return realSections.toList()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.dropbox.affectedmoduledetector.util

import java.io.File

/**
* Returns an OS specific path respecting the separator character for the operating system.
*
* The Git client appears to only talk Unix-like paths however the Gradle client understands all
* OS path variations. This causes issues on systems other than those that use the "/" path
* character i.e. Windows. Therefore we need to normalise the path.
*/
fun String.toOsSpecificPath(): String {
return this.split("/").joinToString(File.separator)
}

/**
* Returns a String with an OS specific line endings for the operating system.
*
* The Git client appears to only talk Unix-like line endings ("\n") however the Gradle client
* understands all OS line ending variants. This causes issues on systems other than those that
* use Unix-like line endings i.e. Windows. Therefore we need to normalise the line endings.
*/
fun String.toOsSpecificLineEnding(): String {
return this.replace("\n", System.lineSeparator())
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class AffectedModuleConfigurationTest {
@Test
fun `GIVEN AffectedModuleConfiguration WHEN log folder is set THEN log folder is set`() {
// GIVEN
val sample = "sammple"
val sample = "sample"

// WHEN
config.logFolder = sample
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ class AffectedModuleDetectorImplTest {
@JvmField
val attachLogsRule = AttachLogsTestRule()
private val logger = attachLogsRule.logger

@Rule
@JvmField
val tmpFolder = TemporaryFolder()

@Rule
@JvmField
val tmpFolder2 = TemporaryFolder()
Expand All @@ -43,10 +45,10 @@ class AffectedModuleDetectorImplTest {
private lateinit var p12: Project
private lateinit var p13: Project
private val pathsAffectingAllModules = setOf(
"tools/android/buildSrc",
"android/gradlew",
"android/gradle",
"dbx/core/api/"
convertToFilePath("tools", "android", "buildSrc"),
convertToFilePath("android", "gradlew"),
convertToFilePath("android", "gradle"),
convertToFilePath("dbx", "core", "api")
)
private lateinit var affectedModuleConfiguration: AffectedModuleConfiguration

Expand Down Expand Up @@ -81,18 +83,21 @@ class AffectedModuleDetectorImplTest {
*/

// Root projects
root = ProjectBuilder.builder()
.withProjectDir(tmpDir)
.withName("root")
.build()
// Project Graph expects supportRootFolder.
(root.properties.get("ext") as ExtraPropertiesExtension).set("supportRootFolder", tmpDir)
(root.properties["ext"] as ExtraPropertiesExtension).set("supportRootFolder", tmpDir)
root2 = ProjectBuilder.builder()
.withProjectDir(tmpDir2)
.withName("root2/ui")
.build()
// Project Graph expects supportRootFolder.
(root2.properties.get("ext") as ExtraPropertiesExtension).set("supportRootFolder", tmpDir2)
(root2.properties["ext"] as ExtraPropertiesExtension).set("supportRootFolder", tmpDir2)

// Library modules
p1 = ProjectBuilder.builder()
.withProjectDir(tmpDir.resolve("p1"))
.withName("p1")
Expand All @@ -104,29 +109,29 @@ class AffectedModuleDetectorImplTest {
.withParent(root)
.build()
p3 = ProjectBuilder.builder()
.withProjectDir(tmpDir.resolve("p1:p3"))
.withProjectDir(tmpDir.resolve("p1/p3"))
.withName("p3")
.withParent(p1)
.build()
val p3config = p3.configurations.create("p3config")
p3config.dependencies.add(p3.dependencies.project(mutableMapOf("path" to ":p1")))
p4 = ProjectBuilder.builder()
.withProjectDir(tmpDir.resolve("p1:p3:p4"))
.withProjectDir(tmpDir.resolve("p1/p3/p4"))
.withName("p4")
.withParent(p3)
.build()
val p4config = p4.configurations.create("p4config")
p4config.dependencies.add(p4.dependencies.project(mutableMapOf("path" to ":p1:p3")))
p5 = ProjectBuilder.builder()
.withProjectDir(tmpDir.resolve("p2:p5"))
.withProjectDir(tmpDir.resolve("p2/p5"))
.withName("p5")
.withParent(p2)
.build()
val p5config = p5.configurations.create("p5config")
p5config.dependencies.add(p5.dependencies.project(mutableMapOf("path" to ":p2")))
p5config.dependencies.add(p5.dependencies.project(mutableMapOf("path" to ":p1:p3")))
p6 = ProjectBuilder.builder()
.withProjectDir(tmpDir.resolve("p1:p3:p6"))
.withProjectDir(tmpDir.resolve("p1/p3/p6"))
.withName("p6")
.withParent(p3)
.build()
Expand All @@ -152,6 +157,8 @@ class AffectedModuleDetectorImplTest {
.withName("benchmark")
.withParent(root)
.build()

// UI modules
p12 = ProjectBuilder.builder()
.withProjectDir(tmpDir2.resolve("compose"))
.withName("compose")
Expand Down Expand Up @@ -630,6 +637,7 @@ class AffectedModuleDetectorImplTest {
)
)
}

@Test
fun changeInNormalOnlyDependent_normalBuild() {
val detector = AffectedModuleDetectorImpl(
Expand Down
Loading

0 comments on commit d205199

Please sign in to comment.