diff options
| author | Chick Markley | 2019-07-31 15:41:05 -0700 |
|---|---|---|
| committer | Schuyler Eldridge | 2019-07-31 18:41:05 -0400 |
| commit | 86d2470d8294a4dba37d33ba021558ba33da4d65 (patch) | |
| tree | 250319d0d32326952c86ec798ee27e8e5e06a3f8 /src | |
| parent | 3ef04a7f704c9f586bdb0841a3b953a032c77622 (diff) | |
io.Source is not closed when used in most common text reading idiom (#1142)
- Provide new tools for reading in text
- from a file
- from a string file name
- from a resource file
- text can be read in as
- a single string with newlines
- a seq of strings, one string per line
- FileUtils put in its own file
- in same package to keep existing API the same
Hopefully this will protect users from creating resource leaks that
may explain sbt crashing.
Also simpler interface should make code more streamlined, existing
uses of io.Source seem often to be converting between text and seq
unnecessarily.
Add note of caution on requiring slash on resource readers
Diffstat (limited to 'src')
| -rw-r--r-- | src/main/scala/firrtl/Driver.scala | 87 | ||||
| -rw-r--r-- | src/main/scala/firrtl/FileUtils.scala | 168 |
2 files changed, 170 insertions, 85 deletions
diff --git a/src/main/scala/firrtl/Driver.scala b/src/main/scala/firrtl/Driver.scala index 4915079f..f68a2035 100644 --- a/src/main/scala/firrtl/Driver.scala +++ b/src/main/scala/firrtl/Driver.scala @@ -5,7 +5,6 @@ package firrtl import scala.collection._ import scala.util.{Failure, Try} import java.io.{File, FileNotFoundException} -import scala.sys.process.{BasicIO, ProcessLogger, stringSeqToProcess} import net.jcazevedo.moultingyaml._ import annotations._ import firrtl.annotations.AnnotationYamlProtocol._ @@ -15,6 +14,7 @@ import firrtl.stage.{FirrtlExecutionResultView, FirrtlStage} import firrtl.stage.phases.DriverCompatibility import firrtl.options.{StageUtils, Phase, Viewer} import firrtl.options.phases.DeletedWrapper +import firrtl.FileUtils.getText /** @@ -112,7 +112,7 @@ object Driver { JsonProtocol.deserializeTry(file).recoverWith { case jsonException => // Try old protocol if new one fails Try { - val yaml = io.Source.fromFile(file).getLines().mkString("\n").parseYaml + val yaml = getText(file).parseYaml val result = yaml.convertTo[List[LegacyAnnotation]] val msg = s"$file is a YAML file!\n" + (" "*9) + "YAML Annotation files are deprecated! Use JSON" @@ -259,86 +259,3 @@ object Driver { } } -object FileUtils { - - /** Create a directory if it doesn't exist - * @param directoryName a directory string with one or more levels - * @return true if the directory exists or if it was successfully created - */ - def makeDirectory(directoryName: String): Boolean = { - val dirFile = new File(directoryName) - if(dirFile.exists()) { - dirFile.isDirectory - } else { - dirFile.mkdirs() - } - } - - /** - * recursively delete all directories in a relative path - * DO NOT DELETE absolute paths - * - * @param directoryPathName a directory hierarchy to delete - */ - def deleteDirectoryHierarchy(directoryPathName: String): Boolean = { - deleteDirectoryHierarchy(new File(directoryPathName)) - } - /** - * recursively delete all directories in a relative path - * DO NOT DELETE absolute paths - * - * @param file: a directory hierarchy to delete - */ - def deleteDirectoryHierarchy(file: File, atTop: Boolean = true): Boolean = { - if(file.getPath.split("/").last.isEmpty || - file.getAbsolutePath == "/" || - file.getPath.startsWith("/")) { - StageUtils.dramaticError(s"delete directory ${file.getPath} will not delete absolute paths") - false - } - else { - val result = { - if(file.isDirectory) { - file.listFiles().forall( f => deleteDirectoryHierarchy(f)) && file.delete() - } - else { - file.delete() - } - } - result - } - } - - /** Indicate if an external command (executable) is available (from the current PATH). - * - * @param cmd the command/executable plus any arguments to the command as a Seq(). - * @return true if ```cmd <args>``` returns a 0 exit status. - */ - def isCommandAvailable(cmd: Seq[String]): Boolean = { - // Eat any output. - val sb = new StringBuffer - val ioToDevNull = BasicIO(withIn = false, ProcessLogger(line => sb.append(line))) - - try { - cmd.run(ioToDevNull).exitValue == 0 - } catch { - case e: Throwable => false - } - } - - /** Indicate if an external command (executable) is available (from the current PATH). - * - * @param cmd the command/executable (without any arguments). - * @return true if ```cmd``` returns a 0 exit status. - */ - def isCommandAvailable(cmd:String): Boolean = { - isCommandAvailable(Seq(cmd)) - } - - /** Flag indicating if vcs is available (for Verilog compilation and testing). - * We used to use a bash command (`which ...`) to determine this, but this is problematic on Windows (issue #807). - * Instead we try to run the executable itself (with innocuous arguments) and interpret any errors/exceptions - * as an indication that the executable is unavailable. - */ - lazy val isVCSAvailable: Boolean = isCommandAvailable(Seq("vcs", "-platform")) -} diff --git a/src/main/scala/firrtl/FileUtils.scala b/src/main/scala/firrtl/FileUtils.scala new file mode 100644 index 00000000..b859073d --- /dev/null +++ b/src/main/scala/firrtl/FileUtils.scala @@ -0,0 +1,168 @@ +// See LICENSE for license details. + +package firrtl + +import java.io.File + +import firrtl.options.StageUtils + +import scala.collection.Seq +import scala.sys.process.{BasicIO, ProcessLogger, stringSeqToProcess} + +object FileUtils { + + /** Create a directory if it doesn't exist + * @param directoryName a directory string with one or more levels + * @return true if the directory exists or if it was successfully created + */ + def makeDirectory(directoryName: String): Boolean = { + val dirFile = new File(directoryName) + if(dirFile.exists()) { + dirFile.isDirectory + } else { + dirFile.mkdirs() + } + } + + /** + * recursively delete all directories in a relative path + * DO NOT DELETE absolute paths + * + * @param directoryPathName a directory hierarchy to delete + */ + def deleteDirectoryHierarchy(directoryPathName: String): Boolean = { + deleteDirectoryHierarchy(new File(directoryPathName)) + } + /** + * recursively delete all directories in a relative path + * DO NOT DELETE absolute paths + * + * @param file: a directory hierarchy to delete + */ + def deleteDirectoryHierarchy(file: File, atTop: Boolean = true): Boolean = { + if(file.getPath.split("/").last.isEmpty || + file.getAbsolutePath == "/" || + file.getPath.startsWith("/")) { + StageUtils.dramaticError(s"delete directory ${file.getPath} will not delete absolute paths") + false + } + else { + val result = { + if(file.isDirectory) { + file.listFiles().forall( f => deleteDirectoryHierarchy(f)) && file.delete() + } + else { + file.delete() + } + } + result + } + } + + /** Indicate if an external command (executable) is available (from the current PATH). + * + * @param cmd the command/executable plus any arguments to the command as a Seq(). + * @return true if ```cmd <args>``` returns a 0 exit status. + */ + def isCommandAvailable(cmd: Seq[String]): Boolean = { + // Eat any output. + val sb = new StringBuffer + val ioToDevNull = BasicIO(withIn = false, ProcessLogger(line => sb.append(line))) + + try { + cmd.run(ioToDevNull).exitValue == 0 + } catch { + case _: Throwable => false + } + } + + /** Indicate if an external command (executable) is available (from the current PATH). + * + * @param cmd the command/executable (without any arguments). + * @return true if ```cmd``` returns a 0 exit status. + */ + def isCommandAvailable(cmd:String): Boolean = { + isCommandAvailable(Seq(cmd)) + } + + /** Flag indicating if vcs is available (for Verilog compilation and testing). + * We used to use a bash command (`which ...`) to determine this, but this is problematic on Windows (issue #807). + * Instead we try to run the executable itself (with innocuous arguments) and interpret any errors/exceptions + * as an indication that the executable is unavailable. + */ + lazy val isVCSAvailable: Boolean = isCommandAvailable(Seq("vcs", "-platform")) + + /** Read a text file and return it as a Seq of strings + * Closes the file after read to avoid dangling file handles + * + * @param fileName The file to read + */ + def getLines(fileName: String): Seq[String] = { + val source = io.Source.fromFile(fileName) + val lines = source.getLines() + source.close() + lines.toSeq + } + + /** Read a text file and return it as a Seq of strings + * Closes the file after read to avoid dangling file handles + * + * @param file a java File to be read + */ + def getLines(file: File): Seq[String] = { + val source = io.Source.fromFile(file) + val lines = source.getLines() + source.close() + lines.toSeq + } + + /** Read a text file and return it as a single string + * Closes the file after read to avoid dangling file handles + * + * @param fileName The file to read + */ + def getText(fileName: String): String = { + val source = io.Source.fromFile(fileName) + val text = source.mkString + source.close() + text + } + + /** Read a text file and return it as a single string + * Closes the file after read to avoid dangling file handles + * + * @param file a java File to be read + */ + def getText(file: File): String = { + val source = io.Source.fromFile(file) + val text = source.mkString + source.close() + text + } + + /** Read text file and return it as a Seq of strings + * Closes the file after read to avoid dangling file handles + * @note resourceName typically begins with a slash. + * + * @param resourceName a java File to be read + */ + def getLinesResource(resourceName: String): Seq[String] = { + val inputStream = getClass.getResourceAsStream(resourceName) + val text = io.Source.fromInputStream(inputStream).getLines().toSeq + inputStream.close() + text + } + + /** Read text file and return it as a single string + * Closes the file after read to avoid dangling file handles + * @note resourceName typically begins with a slash. + * + * @param resourceName a java File to be read + */ + def getTextResource(resourceName: String): String = { + val inputStream = getClass.getResourceAsStream(resourceName) + val text = io.Source.fromInputStream(inputStream).mkString + inputStream.close() + text + } +}
\ No newline at end of file |
