blob: 5070b560a3611f0ee2942fb7ef48b47c7a36843e [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
Ben Claytoneef6bc12024-03-19 17:35:08 +0000123// BuildPath looks for the binary output directory at '<dawn>/out/active'.
124// Returns the path if found, otherwise an empty string.
125func BuildPath() string {
126 if dawnRoot := DawnRoot(); dawnRoot != "" {
127 bin := filepath.Join(dawnRoot, "out/active")
128 if info, err := os.Stat(bin); err == nil && info.IsDir() {
129 return bin
130 }
131 }
132 return ""
133}
134
Ben Clayton85965352023-10-30 20:08:49 +0000135// IsDir returns true if the path resolves to a directory
136func IsDir(path string) bool {
137 s, err := os.Stat(path)
138 if err != nil {
139 return false
140 }
141 return s.IsDir()
142}
143
144// IsFile returns true if the path resolves to a file
145func IsFile(path string) bool {
146 s, err := os.Stat(path)
147 if err != nil {
148 return false
149 }
150 return !s.IsDir()
151}
Ben Claytonb171bec2023-11-21 16:10:32 +0000152
153// CommonRootDir returns the common directory for pathA and pathB
154func CommonRootDir(pathA, pathB string) string {
155 pathA, pathB = filepath.ToSlash(pathA), filepath.ToSlash(pathB) // Normalize to forward-slash
156 if !strings.HasSuffix(pathA, "/") {
157 pathA += "/"
158 }
159 if !strings.HasSuffix(pathB, "/") {
160 pathB += "/"
161 }
162 n := len(pathA)
163 if len(pathB) < n {
164 n = len(pathB)
165 }
166 common := ""
167 for i := 0; i < n; i++ {
168 a, b := pathA[i], pathB[i]
169 if a != b {
170 break
171 }
172 if a == '/' {
173 common = pathA[:i+1]
174 }
175 }
176 return common
177}