Skip to content

Commit dfa4ebb

Browse files
committed
fix dependency resolving for dependencies of dependencies needing a local dependency
1 parent 90ea37b commit dfa4ebb

2 files changed

Lines changed: 58 additions & 11 deletions

File tree

src/main/scala/io/viash/config/dependencies/Dependency.scala

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import java.nio.file.Files
2424
import io.viash.ViashNamespace
2525
import io.viash.exceptions.MissingBuildYamlException
2626
import io.viash.config.ScopeEnum
27+
import io.viash.helpers.Logging
2728

2829
@description(
2930
"""Specifies a Viash component (script or executable) that should be made available for the code defined in the component.
@@ -140,7 +141,7 @@ case class Dependency(
140141
}.getOrElse(false)
141142
}
142143

143-
object Dependency {
144+
object Dependency extends Logging {
144145

145146
/**
146147
* Relativize the writtenPath info from a dependency to a source and destination path so it can be copied.
@@ -153,10 +154,34 @@ object Dependency {
153154
* @param mainDependency Top level dependency for which optionally dependencies of dependencies are being resolved. Used to relativize paths
154155
* @return Tuple with source and destination paths, relativized to current repository locations, ready to be copied
155156
*/
156-
def getSourceAndDestinationFromWrittenPath(dependencyPath: String, output: Path, repoPath: Path, mainDependency: Dependency): (Path, Path) = {
157+
def getSourceAndDestinationFromWrittenPath(dependencyPath: String, output: Path, repoPath: Path, mainDependency: Dependency, remoteLocalDependencyResolver: Option[(Path, Path)]): (Path, Path) = {
157158
import scala.jdk.CollectionConverters._
158159

159-
val sourcePath = repoPath.resolve(dependencyPath)
160+
val defaultSourcePath = repoPath.resolve(dependencyPath)
161+
val sourcePath = if (defaultSourcePath.toFile().exists()) {
162+
// If the dependencyPath is a valid path, use it as source
163+
defaultSourcePath
164+
} else if (remoteLocalDependencyResolver.isDefined) {
165+
logger.debug(s"Couldn't find sourcePath, using remote local dependency resolver for $dependencyPath")
166+
logger.debug(s"Remote local dependency resolver: $remoteLocalDependencyResolver")
167+
val alternativeSourcePath = remoteLocalDependencyResolver.get._1
168+
val alternativeTargetPath = remoteLocalDependencyResolver.get._2
169+
170+
// strips alternativeTargetPath from dependencyPath
171+
val targetIter = alternativeTargetPath.iterator().asScala.toList.map(p => Some(p))
172+
val depIter = Paths.get(dependencyPath).iterator().asScala.toList.map(p => Some(p))
173+
val zipped = depIter.zipAll(targetIter, None, None).dropWhile {
174+
case (depPart, targetPart) => depPart == targetPart
175+
}
176+
val relativePath = Paths.get(zipped.flatMap(_._1).mkString("/"))
177+
logger.debug(s"relativePath: $relativePath")
178+
val res = alternativeSourcePath.resolve(relativePath)
179+
logger.debug(s"Using alternative source path: $res")
180+
res
181+
} else {
182+
// Otherwise, throw an error
183+
throw new MissingBuildYamlException(defaultSourcePath, mainDependency)
184+
}
160185
// Split the path into chunks so we can manipulate them more easily
161186
val pathParts = Paths.get(dependencyPath).iterator().asScala.toList.map(p => p.toString())
162187

src/main/scala/io/viash/helpers/DependencyResolver.scala

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ object DependencyResolver extends Logging {
266266
}
267267

268268
// Read a config file from a built target. Extract dependencies 'writtenPath'.
269-
def getSparseDependencyInfo(configPath: String): List[String] = {
269+
def getSparseDependencyInfo(configPath: String): (List[String], String) = {
270270
try {
271271
val yamlText = IO.read(IO.uri(configPath))
272272
val json = Convert.textToJson(yamlText, configPath)
@@ -275,15 +275,18 @@ object DependencyResolver extends Logging {
275275
val dependencies =
276276
if (legacyMode) {
277277
val jsonVec = json.hcursor.downField("functionality").downField("dependencies").focus.flatMap(_.asArray).get
278-
jsonVec.flatMap(_.hcursor.downField("writtenPath").as[String].toOption).toList
278+
val depList = jsonVec.flatMap(_.hcursor.downField("writtenPath").as[String].toOption).toList
279+
(depList, "")
279280
}
280281
else {
281282
val jsonVec = json.hcursor.downField("build_info").downField("dependencies").focus.flatMap(_.asArray).get
282-
jsonVec.flatMap(_.hcursor.as[String].toOption).toList
283+
val depList = jsonVec.flatMap(_.hcursor.as[String].toOption).toList
284+
val outputPath = json.hcursor.downField("build_info").downField("output").as[String].toOption.get
285+
(depList, outputPath)
283286
}
284287
dependencies
285288
} catch {
286-
case _: Throwable => Nil
289+
case _: Throwable => (Nil, "")
287290
}
288291
}
289292

@@ -296,19 +299,38 @@ object DependencyResolver extends Logging {
296299
}
297300

298301
// Handle dependencies of dependencies. For a given already built component, get their dependencies, copy them to our new target folder and recurse into these.
299-
def recurseBuiltDependencies(output: Path, repoPath: Path, builtDependencyPath: String, dependency: Dependency, depth: Int = 0): Unit = {
302+
def recurseBuiltDependencies(output: Path, repoPath: Path, builtDependencyPath: String, dependency: Dependency, dependencySourcePath: Option[Path] = None, depth: Int = 0): Unit = {
303+
import scala.jdk.CollectionConverters._
300304

301305
// Limit recursion depth to prevent infinite loops in e.g. cross dependencies (TODO)
302306
if (depth > 10)
303307
throw new RuntimeException("Copying dependencies traces too deep. Possibibly caused by a cross dependency.")
304308

305309
// this returns paths relative to `repoPath` of dependencies to be copied to `output`
306-
val dependencyPaths = getSparseDependencyInfo(builtDependencyPath + "/.config.vsh.yaml")
310+
val (dependencyPaths, relativeOutput) = getSparseDependencyInfo(builtDependencyPath + "/.config.vsh.yaml")
311+
logger.debug(s"Paths to relativize: dependencySourcePath: $dependencySourcePath, relativeOutput: $relativeOutput")
312+
313+
// remove the trailing path parts as far as relativeOutputPath matches the dependencySourcePath
314+
// baseDependencySourcePath: a/b/c/d/e
315+
// relativeOutputPath: c'/d/e
316+
// output: a/b/c & c'
317+
val dependencySourceParts = dependencySourcePath.map { dsp =>
318+
val dspParts = dsp.iterator().asScala.toList.map(p => Some(p)).reverse
319+
val relativeOutputPath = Paths.get(relativeOutput).iterator().asScala.toList.map(p => Some(p)).reverse
320+
// Find the first part that is not in the relative output path
321+
val commonParts = dspParts.zipAll(relativeOutputPath, None, None).dropWhile{ case (a, b) => a == b }
322+
323+
val leftPath = commonParts.flatMap(_._1).reverse.fold(dsp.getRoot())((p1, p2) => p1.resolve(p2))
324+
val rightPath = commonParts.flatMap(_._2).reverse.reduce((p1, p2) => p1.resolve(p2))
325+
326+
(leftPath, rightPath)
327+
}
328+
logger.debug(s"dependencySourceParts: $dependencySourceParts")
307329

308330
for (dp <- dependencyPaths) {
309331
// Get the source & destination path for the dependency, functionality depends whether it was a previous dependency or not.
310332
// Paths are relativized depending the original dependency.
311-
val (sourcePath, destPath) = Dependency.getSourceAndDestinationFromWrittenPath(dp, output, repoPath, dependency)
333+
val (sourcePath, destPath) = Dependency.getSourceAndDestinationFromWrittenPath(dp, output, repoPath, dependency, dependencySourceParts)
312334

313335
// Make sure the destination is clean so first remove the destination folder if it exists
314336
if (destPath.toFile().exists())
@@ -319,7 +341,7 @@ object DependencyResolver extends Logging {
319341
IO.copyFolder(sourcePath, destPath)
320342

321343
// Check for more dependencies
322-
recurseBuiltDependencies(output, repoPath, destPath.toString(), dependency, depth + 1)
344+
recurseBuiltDependencies(output, repoPath, destPath.toString(), dependency, Some(sourcePath), depth + 1)
323345
}
324346
}
325347

0 commit comments

Comments
 (0)