aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/firrtl/FileUtils.scala
blob: 5f825a383ab20eb82f3717922c56950341b80e21 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
// SPDX-License-Identifier: Apache-2.0

package firrtl

import firrtl.options.StageUtils

import scala.collection.Seq
import scala.sys.process.{stringSeqToProcess, BasicIO, ProcessLogger}

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 dirPath = getPath(directoryName)
    if (os.exists(dirPath)) {
      os.isDir(dirPath)
    } else {
      os.makeDir.all(dirPath)
      true
    }
  }

  /**
    * 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 = {
    os.FilePath(directoryPathName) match {
      case path: os.Path =>
        StageUtils.dramaticError(s"delete directory $path will not delete absolute paths")
        false
      case rel: os.RelPath =>
        val path = os.pwd / rel
        os.exists(path) && { os.remove.all(path); true }
      case sub: os.SubPath =>
        val path = os.pwd / sub
        os.exists(path) && { os.remove.all(path); true }
    }
  }

  /**
    * recursively delete all directories in a relative path
    * DO NOT DELETE absolute paths
    *
    * @param file: a directory hierarchy to delete
    */
  @deprecated("Use os-lib instead, this function will be removed in FIRRTL 1.6", "FIRRTL 1.5")
  def deleteDirectoryHierarchy(file: java.io.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")) || isCommandAvailable(Seq("vcs", "-full64", "-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] = getLines(getPath(fileName))

  /** Read a text file and return it as  a Seq of strings
    * Closes the file after read to avoid dangling file handles
    * @param file an os.Path to be read
    */
  def getLines(file: os.Path): Seq[String] = os.read.lines(file)

  /** 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
    */
  @deprecated("Use os-lib instead, this function will be removed in FIRRTL 1.6", "FIRRTL 1.5")
  def getLines(file: java.io.File): Seq[String] = {
    val source = scala.io.Source.fromFile(file)
    val lines = source.getLines().toList
    source.close()
    lines
  }

  /** 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 = getText(getPath(fileName))

  /** Read a text file and return it as  a single string
    * Closes the file after read to avoid dangling file handles
    *
    * @param file an os.Path to be read
    */
  def getText(file: os.Path): String = os.read(file)

  /** 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
    */
  @deprecated("Use os-lib instead, this function will be removed in FIRRTL 1.6", "FIRRTL 1.5")
  def getText(file: java.io.File): String = {
    val source = scala.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)
    // the .toList at the end is critical to force stream to be read.
    // Without it the lazy evaluation can cause failure in MultiThreadingSpec
    val text = scala.io.Source.fromInputStream(inputStream).getLines().toList
    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 = scala.io.Source.fromInputStream(inputStream).mkString
    inputStream.close()
    text
  }

  /** Get os.Path from String
    * @param pathName an absolute or relative path string
    */
  def getPath(pathName: String): os.Path = os.FilePath(pathName) match {
    case path: os.Path    => path
    case sub:  os.SubPath => os.pwd / sub
    case rel:  os.RelPath => os.pwd / rel
  }
}