util.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. // Copyright (c) 2024 The fileutil Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // Package ccgo collects utilities often used while generating code with ccgo.
  5. package ccgo // import "modernc.org/fileutil/ccgo"
  6. import (
  7. "archive/tar"
  8. "bufio"
  9. "bytes"
  10. "compress/gzip"
  11. "context"
  12. "fmt"
  13. "io"
  14. "io/ioutil"
  15. "os"
  16. "os/exec"
  17. "path/filepath"
  18. "runtime/debug"
  19. "strings"
  20. "time"
  21. )
  22. // CopyFile copies src to dest, preserving permissions and times where/when
  23. // possible. If canOverwrite is not nil, it is consulted whether a destination
  24. // file can be overwritten. If canOverwrite is nil then destination is
  25. // overwritten if permissions allow that, otherwise the function fails.
  26. //
  27. // Src and dst must be in the slash form.
  28. func CopyFile(dst, src string, canOverwrite func(fn string, fi os.FileInfo) bool) (n int64, rerr error) {
  29. dst = filepath.FromSlash(dst)
  30. dstDir := filepath.Dir(dst)
  31. di, err := os.Stat(dstDir)
  32. switch {
  33. case err != nil:
  34. if !os.IsNotExist(err) {
  35. return 0, err
  36. }
  37. if err := os.MkdirAll(dstDir, 0770); err != nil {
  38. return 0, err
  39. }
  40. case err == nil:
  41. if !di.IsDir() {
  42. return 0, fmt.Errorf("cannot create directory, file exists: %s", dst)
  43. }
  44. }
  45. src = filepath.FromSlash(src)
  46. si, err := os.Stat(src)
  47. if err != nil {
  48. return 0, err
  49. }
  50. if si.IsDir() {
  51. return 0, fmt.Errorf("cannot copy a directory: %s", src)
  52. }
  53. di, err = os.Stat(dst)
  54. switch {
  55. case err != nil && !os.IsNotExist(err):
  56. return 0, err
  57. case err == nil:
  58. if di.IsDir() {
  59. return 0, fmt.Errorf("cannot overwite a directory: %s", dst)
  60. }
  61. if canOverwrite != nil && !canOverwrite(dst, di) {
  62. return 0, fmt.Errorf("cannot overwite: %s", dst)
  63. }
  64. }
  65. s, err := os.Open(src)
  66. if err != nil {
  67. return 0, err
  68. }
  69. defer s.Close()
  70. r := bufio.NewReader(s)
  71. d, err := os.Create(dst)
  72. defer func() {
  73. if err := d.Close(); err != nil && rerr == nil {
  74. rerr = err
  75. return
  76. }
  77. if err := os.Chmod(dst, si.Mode()); err != nil && rerr == nil {
  78. rerr = err
  79. return
  80. }
  81. if err := os.Chtimes(dst, si.ModTime(), si.ModTime()); err != nil && rerr == nil {
  82. rerr = err
  83. return
  84. }
  85. }()
  86. w := bufio.NewWriter(d)
  87. defer func() {
  88. if err := w.Flush(); err != nil && rerr == nil {
  89. rerr = err
  90. }
  91. }()
  92. return io.Copy(w, r)
  93. }
  94. // MustCopyFile is like CopyFile but it executes Fatal(stackTrace, err) if it fails.
  95. func MustCopyFile(stackTrace bool, dst, src string, canOverwrite func(fn string, fi os.FileInfo) bool) int64 {
  96. n, err := CopyFile(dst, src, canOverwrite)
  97. if err != nil {
  98. Fatal(stackTrace, err)
  99. }
  100. return n
  101. }
  102. // CopyDir recursively copies src to dest, preserving permissions and times
  103. // where/when possible. If canOverwrite is not nil, it is consulted whether a
  104. // destination file can be overwritten. If canOverwrite is nil then destination
  105. // is overwritten if permissions allow that, otherwise the function fails.
  106. //
  107. // Src and dst must be in the slash form.
  108. func CopyDir(dst, src string, canOverwrite func(fn string, fi os.FileInfo) bool) (files int, bytes int64, rerr error) {
  109. dst = filepath.FromSlash(dst)
  110. src = filepath.FromSlash(src)
  111. si, err := os.Stat(src)
  112. if err != nil {
  113. return 0, 0, err
  114. }
  115. if !si.IsDir() {
  116. return 0, 0, fmt.Errorf("cannot copy a file: %s", src)
  117. }
  118. return files, bytes, filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
  119. if err != nil {
  120. return err
  121. }
  122. if info.Mode()&os.ModeSymlink != 0 {
  123. target, err := filepath.EvalSymlinks(path)
  124. if err != nil {
  125. return fmt.Errorf("cannot evaluate symlink %s: %v", path, err)
  126. }
  127. if info, err = os.Stat(target); err != nil {
  128. return fmt.Errorf("cannot stat %s: %v", target, err)
  129. }
  130. if info.IsDir() {
  131. rel, err := filepath.Rel(src, path)
  132. if err != nil {
  133. return err
  134. }
  135. dst2 := filepath.Join(dst, rel)
  136. if err := os.MkdirAll(dst2, 0770); err != nil {
  137. return err
  138. }
  139. f, b, err := CopyDir(dst2, target, canOverwrite)
  140. files += f
  141. bytes += b
  142. return err
  143. }
  144. path = target
  145. }
  146. rel, err := filepath.Rel(src, path)
  147. if err != nil {
  148. return err
  149. }
  150. if info.IsDir() {
  151. return os.MkdirAll(filepath.Join(dst, rel), 0770)
  152. }
  153. n, err := CopyFile(filepath.Join(dst, rel), path, canOverwrite)
  154. if err != nil {
  155. return err
  156. }
  157. files++
  158. bytes += n
  159. return nil
  160. })
  161. }
  162. // MustCopyDir is like CopyDir, but it executes Fatal(stackTrace, errú if it fails.
  163. func MustCopyDir(stackTrace bool, dst, src string, canOverwrite func(fn string, fi os.FileInfo) bool) (files int, bytes int64) {
  164. file, bytes, err := CopyDir(dst, src, canOverwrite)
  165. if err != nil {
  166. Fatal(stackTrace, err)
  167. }
  168. return file, bytes
  169. }
  170. // UntarFile extracts a named tar.gz archive into dst. If canOverwrite is not
  171. // nil, it is consulted whether a destination file can be overwritten. If
  172. // canOverwrite is nil then destination is overwritten if permissions allow
  173. // that, otherwise the function fails.
  174. //
  175. // Src and dst must be in the slash form.
  176. func UntarFile(dst, src string, canOverwrite func(fn string, fi os.FileInfo) bool) error {
  177. f, err := os.Open(filepath.FromSlash(src))
  178. if err != nil {
  179. return err
  180. }
  181. defer f.Close()
  182. return Untar(dst, bufio.NewReader(f), canOverwrite)
  183. }
  184. // MustUntarFile is like UntarFile but it executes Fatal(stackTrace, err) if it fails.
  185. func MustUntarFile(stackTrace bool, dst, src string, canOverwrite func(fn string, fi os.FileInfo) bool) {
  186. if err := UntarFile(dst, src, canOverwrite); err != nil {
  187. Fatal(stackTrace, err)
  188. }
  189. }
  190. // Untar extracts a tar.gz archive into dst. If canOverwrite is not nil, it is
  191. // consulted whether a destination file can be overwritten. If canOverwrite is
  192. // nil then destination is overwritten if permissions allow that, otherwise the
  193. // function fails.
  194. //
  195. // Dst must be in the slash form.
  196. func Untar(dst string, r io.Reader, canOverwrite func(fn string, fi os.FileInfo) bool) error {
  197. dst = filepath.FromSlash(dst)
  198. gr, err := gzip.NewReader(r)
  199. if err != nil {
  200. return err
  201. }
  202. tr := tar.NewReader(gr)
  203. for {
  204. hdr, err := tr.Next()
  205. if err != nil {
  206. if err != io.EOF {
  207. return err
  208. }
  209. return nil
  210. }
  211. switch hdr.Typeflag {
  212. case tar.TypeDir:
  213. dir := filepath.Join(dst, hdr.Name)
  214. if err = os.MkdirAll(dir, 0770); err != nil {
  215. return err
  216. }
  217. case tar.TypeSymlink, tar.TypeXGlobalHeader:
  218. // skip
  219. case tar.TypeReg, tar.TypeRegA:
  220. dir := filepath.Dir(filepath.Join(dst, hdr.Name))
  221. if _, err := os.Stat(dir); err != nil {
  222. if !os.IsNotExist(err) {
  223. return err
  224. }
  225. if err = os.MkdirAll(dir, 0770); err != nil {
  226. return err
  227. }
  228. }
  229. fn := filepath.Join(dst, hdr.Name)
  230. f, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY, os.FileMode(hdr.Mode))
  231. if err != nil {
  232. return err
  233. }
  234. w := bufio.NewWriter(f)
  235. if _, err = io.Copy(w, tr); err != nil {
  236. return err
  237. }
  238. if err := w.Flush(); err != nil {
  239. return err
  240. }
  241. if err := f.Close(); err != nil {
  242. return err
  243. }
  244. if err := os.Chtimes(fn, hdr.AccessTime, hdr.ModTime); err != nil {
  245. return err
  246. }
  247. default:
  248. return fmt.Errorf("unexpected tar header typeflag %#02x", hdr.Typeflag)
  249. }
  250. }
  251. }
  252. // MustUntar is like Untar but it executes Fatal(stackTrace, err) if it fails.
  253. func MustUntar(stackTrace bool, dst string, r io.Reader, canOverwrite func(fn string, fi os.FileInfo) bool) {
  254. if err := Untar(dst, r, canOverwrite); err != nil {
  255. Fatal(stackTrace, err)
  256. }
  257. }
  258. // Fatalf prints a formatted message to os.Stderr and performs os.Exit(1). A
  259. // stack trace is added if stackTrace is true.
  260. func Fatalf(stackTrace bool, s string, args ...interface{}) {
  261. if stackTrace {
  262. fmt.Fprintf(os.Stderr, "%s\n", debug.Stack())
  263. }
  264. fmt.Fprintln(os.Stderr, strings.TrimSpace(fmt.Sprintf(s, args...)))
  265. os.Exit(1)
  266. }
  267. // Fatal prints its argumenst to os.Stderr and performs os.Exit(1). A
  268. // stack trace is added if stackTrace is true.
  269. func Fatal(stackTrace bool, args ...interface{}) {
  270. if stackTrace {
  271. fmt.Fprintf(os.Stderr, "%s\n", debug.Stack())
  272. }
  273. fmt.Fprintln(os.Stderr, strings.TrimSpace(fmt.Sprint(args...)))
  274. os.Exit(1)
  275. }
  276. // Mkdirs will create all paths. Paths must be in slash form.
  277. func Mkdirs(paths ...string) error {
  278. for _, path := range paths {
  279. path = filepath.FromSlash(path)
  280. if err := os.MkdirAll(path, 0770); err != nil {
  281. return err
  282. }
  283. }
  284. return nil
  285. }
  286. // MustMkdirs is like Mkdir but if executes Fatal(stackTrace, err) if it fails.
  287. func MustMkdirs(stackTrace bool, paths ...string) {
  288. if err := Mkdirs(paths...); err != nil {
  289. Fatal(stackTrace, err)
  290. }
  291. }
  292. // InDir executes f in dir. Dir must be in slash form.
  293. func InDir(dir string, f func() error) (err error) {
  294. var cwd string
  295. if cwd, err = os.Getwd(); err != nil {
  296. return err
  297. }
  298. defer func() {
  299. if err2 := os.Chdir(cwd); err2 != nil {
  300. err = err2
  301. }
  302. }()
  303. if err = os.Chdir(filepath.FromSlash(dir)); err != nil {
  304. return err
  305. }
  306. return f()
  307. }
  308. // MustInDir is like InDir but it executes Fatal(stackTrace, err) if it fails.
  309. func MustInDir(stackTrace bool, dir string, f func() error) {
  310. if err := InDir(dir, f); err != nil {
  311. Fatal(stackTrace, err)
  312. }
  313. }
  314. type echoWriter struct {
  315. w bytes.Buffer
  316. }
  317. func (w *echoWriter) Write(b []byte) (int, error) {
  318. os.Stdout.Write(b)
  319. return w.w.Write(b)
  320. }
  321. // Shell echoes and executes cmd with args and returns the combined output if
  322. // the command. Passing nil is ok.
  323. func Shell(ctx context.Context, cmd string, args ...string) ([]byte, error) {
  324. cmd, err := exec.LookPath(cmd)
  325. if err != nil {
  326. return nil, err
  327. }
  328. wd, err := AbsCwd()
  329. if err != nil {
  330. return nil, err
  331. }
  332. if ctx == nil {
  333. ctx = context.Background()
  334. }
  335. fmt.Printf("execute %s %q in %s\n", cmd, args, wd)
  336. var b echoWriter
  337. c := exec.CommandContext(ctx, cmd, args...)
  338. c.Stdout = &b
  339. c.Stderr = &b
  340. c.WaitDelay = 5 * time.Second
  341. err = c.Run()
  342. return b.w.Bytes(), err
  343. }
  344. // MustShell is like Shell but it executes Fatal(stackTrace, err) if it fails.
  345. func MustShell(stackTrace bool, ctx context.Context, cmd string, args ...string) []byte {
  346. b, err := Shell(ctx, cmd, args...)
  347. if err != nil {
  348. Fatalf(stackTrace, "%v %s\noutput: %s\nerr: %s", cmd, args, b, err)
  349. }
  350. return b
  351. }
  352. // AbsCwd returns the absolute working directory.
  353. func AbsCwd() (string, error) {
  354. wd, err := os.Getwd()
  355. if err != nil {
  356. return "", err
  357. }
  358. if wd, err = filepath.Abs(wd); err != nil {
  359. return "", err
  360. }
  361. return wd, nil
  362. }
  363. // MustAbsCwd is like AbsCwd but executes Fatal(stackTrace, err) if it fails.
  364. func MustAbsCwd(stackTrace bool) string {
  365. s, err := AbsCwd()
  366. if err != nil {
  367. Fatal(stackTrace, err)
  368. }
  369. return s
  370. }
  371. // Env returns the value of environmental variable key of dflt otherwise.
  372. func Env(key, dflt string) string {
  373. if s := os.Getenv(key); s != "" {
  374. return s
  375. }
  376. return dflt
  377. }
  378. // MustTempDir is like ioutil.TempDir but executes Fatal(stackTrace, err) if it
  379. // fails. The returned path is absolute.
  380. func MustTempDir(stackTrace bool, dir, name string) string {
  381. s, err := ioutil.TempDir(dir, name)
  382. if err != nil {
  383. Fatal(stackTrace, err)
  384. }
  385. if s, err = filepath.Abs(s); err != nil {
  386. Fatal(stackTrace, err)
  387. }
  388. return s
  389. }