Skip to content

Commit

Permalink
Replace better.files.File.newTemporaryDirectory (#5329)
Browse files Browse the repository at this point in the history
* Working on replacing better files

* Replace all instances of better.files.File.newTemporaryDirectory with our own impl

* Rename two functions, remove unused implicit class

* Use Files.writeString, alias better.files instead of java.io.file

* Replace all JFile to use Path instead

* Replace Files.delete with FileUtil.delete

* rename createIfNotExists function, added directory creation as well

* Add linkOption for delete

* remove println
  • Loading branch information
AndreiDreyer authored Feb 27, 2025
1 parent 08c60dc commit ece08fa
Show file tree
Hide file tree
Showing 26 changed files with 539 additions and 333 deletions.
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
package io.joern.console.cpgcreation

import better.files.File
import io.joern.console.workspacehandling.Project
import io.joern.console.{ConsoleException, FrontendConfig, Reporting}
import io.shiftleft.codepropertygraph.generated.Cpg
import io.shiftleft.codepropertygraph.generated.Languages
import flatgraph.help.Table
import flatgraph.help.Table.AvailableWidthProvider

import java.nio.file.Path
import io.joern.x2cpg.utils.FileUtil
import io.joern.x2cpg.utils.FileUtil.*

import java.nio.file.{Path, Files, Paths}

import scala.util.{Failure, Success, Try}

class ImportCode[T <: Project](console: io.joern.console.Console[T])(implicit
Expand All @@ -21,7 +24,7 @@ class ImportCode[T <: Project](console: io.joern.console.Console[T])(implicit
protected val generatorFactory = new CpgGeneratorFactory(config)

private def checkInputPath(inputPath: String): Unit = {
if (!File(inputPath).exists) {
if (!Files.exists(Paths.get(inputPath))) {
throw new ConsoleException(s"Input path does not exist: '$inputPath'")
}
}
Expand Down Expand Up @@ -106,11 +109,11 @@ class ImportCode[T <: Project](console: io.joern.console.Console[T])(implicit

protected def fromFile(inputPath: String, projectName: String = "", args: List[String] = List()): Cpg = {
checkInputPath(inputPath)
if (File(inputPath).isDirectory) {
if (Files.isDirectory(Paths.get(inputPath))) {
throw new ConsoleException(s"Input path is a directory: '$inputPath'")
}
withFileInTmpFile(inputPath) { dir =>
this.apply(dir.pathAsString, projectName, args)
this.apply(dir.toString, projectName, args)
} match {
case Failure(exception) =>
throw new ConsoleException(s"unable to generate cpg from file '$inputPath'", exception)
Expand All @@ -133,7 +136,7 @@ class ImportCode[T <: Project](console: io.joern.console.Console[T])(implicit

def fromString(str: String, args: List[String] = List()): Cpg = {
withCodeInTmpFile(str, s"tmp.$extension") { dir =>
super.apply(dir.pathAsString, args = args)
super.apply(dir.toString, args = args)
} match {
case Failure(exception) => throw new ConsoleException(s"unable to generate cpg from given String", exception)
case Success(value) => value
Expand All @@ -144,7 +147,7 @@ class ImportCode[T <: Project](console: io.joern.console.Console[T])(implicit
class SwiftSrcFrontend(name: String, language: String, description: String, extension: String)
extends SourceBasedFrontend(name, language, description, extension) {
override def apply(inputPath: String, projectName: String, args: List[String]): Cpg = {
if (!File(inputPath).isDirectory) {
if (!Files.isDirectory(Paths.get(inputPath))) {
// The Swift frontend does not support importing a single file.
// Hence, we have to copy it into a temporary folder with super.fromFile and import that folder.
super.fromFile(inputPath, projectName, args)
Expand All @@ -157,7 +160,7 @@ class ImportCode[T <: Project](console: io.joern.console.Console[T])(implicit
class JsFrontend(name: String, language: String, description: String, extension: String)
extends SourceBasedFrontend(name, language, description, extension) {
override def apply(inputPath: String, projectName: String, args: List[String]): Cpg = {
if (!File(inputPath).isDirectory) {
if (!Files.isDirectory(Paths.get(inputPath))) {
// The JS frontend does not support importing a single file.
// Hence, we have to copy it into a temporary folder with super.fromFile and import that folder.
super.fromFile(inputPath, projectName, args)
Expand All @@ -170,23 +173,27 @@ class ImportCode[T <: Project](console: io.joern.console.Console[T])(implicit
class CFrontend(name: String, extension: String = "c")
extends SourceBasedFrontend(name, Languages.NEWC, "Eclipse CDT Based Frontend for C/C++", extension)

private def withCodeInTmpFile(str: String, filename: String)(f: File => Cpg): Try[Cpg] = {
val dir = File.newTemporaryDirectory("console")
private def withCodeInTmpFile(str: String, filename: String)(f: Path => Cpg): Try[Cpg] = {
val dir = Files.createTempDirectory("console")

val result = Try {
(dir / filename).write(str)
Files.writeString((dir / filename), str)
f(dir)
}
dir.deleteOnExit(swallowIOExceptions = true)

FileUtil.deleteOnExit(dir, swallowIOExceptions = true)
result
}

private def withFileInTmpFile(inputPath: String)(f: File => Cpg): Try[Cpg] = {
val dir = File.newTemporaryDirectory("console")
private def withFileInTmpFile(inputPath: String)(f: Path => Cpg): Try[Cpg] = {
val dir = Files.createTempDirectory("console")

val result = Try {
File(inputPath).copyToDirectory(dir)
Paths.get(inputPath).copyToDirectory(dir)
f(dir)
}
dir.deleteOnExit(swallowIOExceptions = true)

FileUtil.deleteOnExit(dir, swallowIOExceptions = true)
result
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import better.files.File
import io.joern.console.FrontendConfig
import io.joern.x2cpg.frontendspecific.jssrc2cpg
import io.joern.x2cpg.passes.frontend.XTypeRecoveryConfig
import io.joern.x2cpg.utils.FileUtil.*
import io.shiftleft.codepropertygraph.generated.Cpg

import java.nio.file.Path
import java.nio.file.{Files, Path, Paths}
import scala.util.Try

case class JsSrcCpgGenerator(config: FrontendConfig, rootPath: Path) extends CpgGenerator {
Expand All @@ -16,11 +17,11 @@ case class JsSrcCpgGenerator(config: FrontendConfig, rootPath: Path) extends Cpg
/** Generate a CPG for the given input path. Returns the output path, or None, if no CPG was generated.
*/
override def generate(inputPath: String, outputPath: String = "cpg.bin.zip"): Try[String] = {
if (File(inputPath).isDirectory) {
if (Files.isDirectory(Paths.get(inputPath))) {
invoke(inputPath, outputPath)
} else {
withFileInTmpFile(inputPath) { dir =>
invoke(dir.pathAsString, outputPath)
invoke(dir.toString, outputPath)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import better.files.File
import io.joern.console.FrontendConfig
import io.joern.x2cpg.frontendspecific.swiftsrc2cpg
import io.joern.x2cpg.passes.frontend.XTypeRecoveryConfig
import io.joern.x2cpg.utils.FileUtil.*
import io.shiftleft.codepropertygraph.generated.Cpg

import java.nio.file.Path
import java.nio.file.{Path, Paths, Files}
import scala.util.Try

case class SwiftSrcCpgGenerator(config: FrontendConfig, rootPath: Path) extends CpgGenerator {
Expand All @@ -18,11 +19,11 @@ case class SwiftSrcCpgGenerator(config: FrontendConfig, rootPath: Path) extends
/** Generate a CPG for the given input path. Returns the output path, or None, if no CPG was generated.
*/
override def generate(inputPath: String, outputPath: String = "cpg.bin.zip"): Try[String] = {
if (File(inputPath).isDirectory) {
if (Files.isDirectory(Paths.get(inputPath))) {
invoke(inputPath, outputPath)
} else {
withFileInTmpFile(inputPath) { dir =>
invoke(dir.pathAsString, outputPath)
invoke(dir.toString, outputPath)
}
}
}
Expand Down
20 changes: 11 additions & 9 deletions console/src/main/scala/io/joern/console/cpgcreation/package.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package io.joern.console

import better.files.File
import better.files.File as BetterFile
import io.joern.x2cpg.utils.FileUtil
import io.joern.x2cpg.utils.FileUtil.*
import io.shiftleft.codepropertygraph.generated.Languages

import java.nio.file.Path
import java.nio.file.{Path, Paths, Files}
import scala.collection.mutable
import scala.util.Try

Expand Down Expand Up @@ -44,7 +46,7 @@ package object cpgcreation {
/** Heuristically determines language by inspecting file/dir at path.
*/
def guessLanguage(path: String): Option[String] = {
val file = File(path)
val file = BetterFile(path)
if (file.isDirectory) {
guessMajorityLanguageInDir(file)
} else {
Expand All @@ -56,7 +58,7 @@ package object cpgcreation {
* files. Rationale: many projects contain files from different languages, but most often one language is standing
* out in numbers.
*/
private def guessMajorityLanguageInDir(directory: File): Option[String] = {
private def guessMajorityLanguageInDir(directory: BetterFile): Option[String] = {
assert(directory.isDirectory, s"$directory must be a directory, but wasn't")
val groupCount = mutable.Map.empty[String, Int].withDefaultValue(0)

Expand Down Expand Up @@ -93,7 +95,7 @@ package object cpgcreation {
private def isCFile(filename: String): Boolean =
Seq(".c", ".cc", ".cpp", ".h", ".hpp", ".hh").exists(filename.endsWith)

private def guessLanguageForRegularFile(file: File): Option[String] = {
private def guessLanguageForRegularFile(file: BetterFile): Option[String] = {
file.name.toLowerCase match {
case f if isJavaBinary(f) => Some(Languages.JAVA)
case f if isCsharpFile(f) => Some(Languages.CSHARPSRC)
Expand All @@ -112,11 +114,11 @@ package object cpgcreation {
}
}

def withFileInTmpFile(inputPath: String)(f: File => Try[String]): Try[String] = {
val dir = File.newTemporaryDirectory("cpgcreation")
File(inputPath).copyToDirectory(dir)
def withFileInTmpFile(inputPath: String)(f: Path => Try[String]): Try[String] = {
val dir = Files.createTempDirectory("cpgcreation")
Paths.get(inputPath).copyToDirectory(dir)
val result = f(dir)
dir.deleteOnExit(swallowIOExceptions = true)
FileUtil.deleteOnExit(dir, swallowIOExceptions = true)
result
}

Expand Down
23 changes: 15 additions & 8 deletions console/src/test/scala/io/joern/console/PluginManagerTests.scala
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package io.joern.console

import better.files.Dsl.*
import better.files.*
import io.shiftleft.utils.ProjectRoot
import io.joern.x2cpg.utils.FileUtil
import io.joern.x2cpg.utils.FileUtil.*
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
import org.scalatest.Ignore
import org.scalatest.Tag

import java.nio.file.Files

import scala.jdk.CollectionConverters.*
import java.nio.file.attribute.PosixFilePermission

class PluginManagerTests extends AnyWordSpec with Matchers {
Expand Down Expand Up @@ -75,15 +79,18 @@ class PluginManagerTests extends AnyWordSpec with Matchers {
object Fixture {

def apply[T]()(f: PluginManager => T): T = {
val dir = File.newTemporaryDirectory("pluginmantests")
mkdir(dir / "lib")
mkdirs(dir / "schema-extender" / "schemas")
val dir = Files.createTempDirectory("pluginmantests")
Files.createDirectory(dir / "lib")
Files.createDirectories(dir / "schema-extender" / "schemas")

val extender = dir / "schema-extender.sh"
val extenderContents = "#!/bin/sh\necho 'foo' > " + (dir / "out.txt")
extender.write(extenderContents)
chmod_+(PosixFilePermission.OWNER_EXECUTE, extender)
val result = f(new PluginManager(dir))
dir.delete()

Files.writeString(extender, extenderContents)
Files.setPosixFilePermissions(extender, Set(PosixFilePermission.OWNER_EXECUTE).asJava)

val result = f(new PluginManager(File(dir)))
FileUtil.delete(dir)
result
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package io.joern.console.workspacehandling

import better.files.Dsl.mkdir
import better.files.File
import io.joern.x2cpg.utils.FileUtil

import java.nio.file.Files
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec

Expand All @@ -12,13 +15,13 @@ class WorkspaceLoaderTests extends AnyWordSpec with Matchers {
"WorkspaceLoader" should {

"create workspace and workspace directory if nonexistent" in {
val dir = File.newTemporaryDirectory(tmpDirPrefix)
dir.delete()
TestLoader().load(dir.path.toString)
val dir = Files.createTempDirectory(tmpDirPrefix)
FileUtil.delete(dir)
TestLoader().load(dir.toString)
try {
dir.exists shouldBe true
Files.exists(dir) shouldBe true
} finally {
dir.delete()
FileUtil.delete(dir)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
package io.joern.c2cpg.io

import better.files.File
import better.files.File as BetterFile
import io.joern.x2cpg.utils.server.FrontendHTTPClient
import io.joern.x2cpg.utils.FileUtil
import io.joern.x2cpg.utils.FileUtil.*

import io.shiftleft.codepropertygraph.cpgloading.CpgLoader
import io.shiftleft.semanticcpg.language.*
import org.scalatest.BeforeAndAfterAll
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec

import java.nio.file.{Files, Path}

import scala.util.Failure
import scala.util.Success
import scala.collection.parallel.CollectionConverters.RangeIsParallelizable
Expand All @@ -16,18 +21,21 @@ class C2CpgHTTPServerTests extends AnyWordSpec with Matchers with BeforeAndAfter

private var port: Int = -1

private def newProjectUnderTest(index: Option[Int] = None): File = {
val dir = File.newTemporaryDirectory("c2cpgTestsHttpTest")
private def newProjectUnderTest(index: Option[Int] = None): Path = {
val dir = Files.createTempDirectory("c2cpgTestsHttpTest")
val file = dir / "main.c"
file.createIfNotExists(createParents = true)
file.createWithParentsIfNotExists(createParents = true)
val indexStr = index.map(_.toString).getOrElse("")
file.writeText(s"""
|int main$indexStr(int argc, char *argv[]) {
| print("Hello World!");
|}
|""".stripMargin)
file.deleteOnExit()
dir.deleteOnExit()
val content = s"""
|int main$indexStr(int argc, char *argv[]) {
| print("Hello World!");
|}
|""".stripMargin

Files.writeString(file, content)
FileUtil.deleteOnExit(file)
FileUtil.deleteOnExit(dir)
dir
}

override def beforeAll(): Unit = {
Expand All @@ -42,10 +50,10 @@ class C2CpgHTTPServerTests extends AnyWordSpec with Matchers with BeforeAndAfter

"Using c2cpg in server mode" should {
"build CPGs correctly (single test)" in {
val cpgOutFile = File.newTemporaryFile("c2cpg.bin")
val cpgOutFile = BetterFile.newTemporaryFile("c2cpg.bin")
cpgOutFile.deleteOnExit()
val projectUnderTest = newProjectUnderTest()
val input = projectUnderTest.path.toAbsolutePath.toString
val input = projectUnderTest.toAbsolutePath.toString
val output = cpgOutFile.toString
val client = FrontendHTTPClient(port)
val req = client.buildRequest(Array(s"input=$input", s"output=$output"))
Expand All @@ -61,10 +69,10 @@ class C2CpgHTTPServerTests extends AnyWordSpec with Matchers with BeforeAndAfter

"build CPGs correctly (multi-threaded test)" in {
(0 until 10).par.foreach { index =>
val cpgOutFile = File.newTemporaryFile("c2cpg.bin")
val cpgOutFile = BetterFile.newTemporaryFile("c2cpg.bin")
cpgOutFile.deleteOnExit()
val projectUnderTest = newProjectUnderTest(Some(index))
val input = projectUnderTest.path.toAbsolutePath.toString
val input = projectUnderTest.toAbsolutePath.toString
val output = cpgOutFile.toString
val client = FrontendHTTPClient(port)
val req = client.buildRequest(Array(s"input=$input", s"output=$output"))
Expand Down
Loading

0 comments on commit ece08fa

Please sign in to comment.