blob: a2edcfb5c91f772f4473e3f9ca7809cae26ee0bc [file] [log] [blame]
Austin Engcc2516a2023-10-17 20:57:54 +00001// Copyright 2022 The Dawn & Tint Authors
Ben Clayton2fb28e02022-04-29 11:18:43 +00002//
Austin Engcc2516a2023-10-17 20:57:54 +00003// Redistribution and use in source and binary forms, with or without
4// modification, are permitted provided that the following conditions are met:
Ben Clayton2fb28e02022-04-29 11:18:43 +00005//
Austin Engcc2516a2023-10-17 20:57:54 +00006// 1. Redistributions of source code must retain the above copyright notice, this
7// list of conditions and the following disclaimer.
Ben Clayton2fb28e02022-04-29 11:18:43 +00008//
Austin Engcc2516a2023-10-17 20:57:54 +00009// 2. Redistributions in binary form must reproduce the above copyright notice,
10// this list of conditions and the following disclaimer in the documentation
11// and/or other materials provided with the distribution.
12//
13// 3. Neither the name of the copyright holder nor the names of its
14// contributors may be used to endorse or promote products derived from
15// this software without specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
21// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Ben Clayton2fb28e02022-04-29 11:18:43 +000027
Ben Clayton68ed8d92022-10-31 18:28:22 +000028package fileutils
Ben Clayton2fb28e02022-04-29 11:18:43 +000029
30import (
31 "fmt"
32 "os"
Ben Clayton7b87e3c2023-10-05 19:51:38 +000033 "os/exec"
Ben Clayton2fb28e02022-04-29 11:18:43 +000034 "path/filepath"
35 "runtime"
36 "strings"
37)
38
39// ThisLine returns the filepath and line number of the calling function
40func ThisLine() string {
41 _, file, line, ok := runtime.Caller(1)
42 if !ok {
43 return ""
44 }
45 return fmt.Sprintf("%v:%v", file, line)
46}
47
48// ThisDir returns the directory of the caller function
49func ThisDir() string {
50 _, file, _, ok := runtime.Caller(1)
51 if !ok {
52 return ""
53 }
54 return filepath.Dir(file)
55}
56
57// DawnRoot returns the path to the dawn project's root directory or empty
58// string if not found.
59func DawnRoot() string {
60 return pathOfFileInParentDirs(ThisDir(), "DEPS")
61}
62
63// pathOfFileInParentDirs looks for file with `name` in paths starting from
64// `path`, and up into parent directories, returning the clean path in which the
65// file is found, or empty string if not found.
66func pathOfFileInParentDirs(path string, name string) string {
67 sep := string(filepath.Separator)
68 path, _ = filepath.Abs(path)
69 numDirs := strings.Count(path, sep) + 1
70 for i := 0; i < numDirs; i++ {
71 test := filepath.Join(path, name)
72 if _, err := os.Stat(test); err == nil {
73 return filepath.Clean(path)
74 }
75
76 path = path + sep + ".."
77 }
78 return ""
79}
80
81// ExpandHome returns the string with all occurrences of '~' replaced with the
82// user's home directory. The the user's home directory cannot be found, then
83// the input string is returned.
84func ExpandHome(path string) string {
85 if strings.ContainsRune(path, '~') {
86 if home, err := os.UserHomeDir(); err == nil {
87 return strings.ReplaceAll(path, "~", home)
88 }
89 }
90 return path
91}
Ben Clayton7b87e3c2023-10-05 19:51:38 +000092
93// NodePath looks for the node binary, first in dawn's third_party directory,
94// falling back to PATH.
95func NodePath() string {
96 if dawnRoot := DawnRoot(); dawnRoot != "" {
97 node := filepath.Join(dawnRoot, "third_party/node")
98 if info, err := os.Stat(node); err == nil && info.IsDir() {
99 path := ""
100 switch fmt.Sprintf("%v/%v", runtime.GOOS, runtime.GOARCH) { // See `go tool dist list`
101 case "darwin/amd64":
102 path = filepath.Join(node, "node-darwin-x64/bin/node")
103 case "darwin/arm64":
104 path = filepath.Join(node, "node-darwin-arm64/bin/node")
105 case "linux/amd64":
106 path = filepath.Join(node, "node-linux-x64/bin/node")
107 case "windows/amd64":
108 path = filepath.Join(node, "node.exe")
109 }
110 if _, err := os.Stat(path); err == nil {
111 return path
112 }
113 }
114 }
115
116 if path, err := exec.LookPath("node"); err == nil {
117 return path
118 }
119
120 return ""
121}
Ben Clayton85965352023-10-30 20:08:49 +0000122
123// IsDir returns true if the path resolves to a directory
124func IsDir(path string) bool {
125 s, err := os.Stat(path)
126 if err != nil {
127 return false
128 }
129 return s.IsDir()
130}
131
132// IsFile returns true if the path resolves to a file
133func IsFile(path string) bool {
134 s, err := os.Stat(path)
135 if err != nil {
136 return false
137 }
138 return !s.IsDir()
139}
Ben Claytonb171bec2023-11-21 16:10:32 +0000140
141// CommonRootDir returns the common directory for pathA and pathB
142func CommonRootDir(pathA, pathB string) string {
143 pathA, pathB = filepath.ToSlash(pathA), filepath.ToSlash(pathB) // Normalize to forward-slash
144 if !strings.HasSuffix(pathA, "/") {
145 pathA += "/"
146 }
147 if !strings.HasSuffix(pathB, "/") {
148 pathB += "/"
149 }
150 n := len(pathA)
151 if len(pathB) < n {
152 n = len(pathB)
153 }
154 common := ""
155 for i := 0; i < n; i++ {
156 a, b := pathA[i], pathB[i]
157 if a != b {
158 break
159 }
160 if a == '/' {
161 common = pathA[:i+1]
162 }
163 }
164 return common
165}