1*8fb009dcSAndroid Build Coastguard Worker// Copyright (c) 2018, Google Inc. 2*8fb009dcSAndroid Build Coastguard Worker// 3*8fb009dcSAndroid Build Coastguard Worker// Permission to use, copy, modify, and/or distribute this software for any 4*8fb009dcSAndroid Build Coastguard Worker// purpose with or without fee is hereby granted, provided that the above 5*8fb009dcSAndroid Build Coastguard Worker// copyright notice and this permission notice appear in all copies. 6*8fb009dcSAndroid Build Coastguard Worker// 7*8fb009dcSAndroid Build Coastguard Worker// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8*8fb009dcSAndroid Build Coastguard Worker// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9*8fb009dcSAndroid Build Coastguard Worker// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 10*8fb009dcSAndroid Build Coastguard Worker// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11*8fb009dcSAndroid Build Coastguard Worker// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 12*8fb009dcSAndroid Build Coastguard Worker// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 13*8fb009dcSAndroid Build Coastguard Worker// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14*8fb009dcSAndroid Build Coastguard Worker 15*8fb009dcSAndroid Build Coastguard Worker//go:build ignore 16*8fb009dcSAndroid Build Coastguard Worker 17*8fb009dcSAndroid Build Coastguard Worker// godeps prints out dependencies of a package in either CMake or Make depfile 18*8fb009dcSAndroid Build Coastguard Worker// format, for incremental rebuilds. 19*8fb009dcSAndroid Build Coastguard Worker// 20*8fb009dcSAndroid Build Coastguard Worker// The depfile format is preferred. It works correctly when new files are added. 21*8fb009dcSAndroid Build Coastguard Worker// However, CMake only supports depfiles for custom commands with Ninja and 22*8fb009dcSAndroid Build Coastguard Worker// starting CMake 3.7. For other configurations, we also support CMake's format, 23*8fb009dcSAndroid Build Coastguard Worker// but CMake must be rerun when file lists change. 24*8fb009dcSAndroid Build Coastguard Workerpackage main 25*8fb009dcSAndroid Build Coastguard Worker 26*8fb009dcSAndroid Build Coastguard Workerimport ( 27*8fb009dcSAndroid Build Coastguard Worker "flag" 28*8fb009dcSAndroid Build Coastguard Worker "fmt" 29*8fb009dcSAndroid Build Coastguard Worker "go/build" 30*8fb009dcSAndroid Build Coastguard Worker "os" 31*8fb009dcSAndroid Build Coastguard Worker "path/filepath" 32*8fb009dcSAndroid Build Coastguard Worker "sort" 33*8fb009dcSAndroid Build Coastguard Worker "strings" 34*8fb009dcSAndroid Build Coastguard Worker) 35*8fb009dcSAndroid Build Coastguard Worker 36*8fb009dcSAndroid Build Coastguard Workervar ( 37*8fb009dcSAndroid Build Coastguard Worker format = flag.String("format", "cmake", "The format to output to, either 'cmake' or 'depfile'") 38*8fb009dcSAndroid Build Coastguard Worker mainPkg = flag.String("pkg", "", "The package to print dependencies for") 39*8fb009dcSAndroid Build Coastguard Worker target = flag.String("target", "", "The name of the output file") 40*8fb009dcSAndroid Build Coastguard Worker out = flag.String("out", "", "The path to write the output to. If unset, this is stdout") 41*8fb009dcSAndroid Build Coastguard Worker) 42*8fb009dcSAndroid Build Coastguard Worker 43*8fb009dcSAndroid Build Coastguard Workerfunc cMakeQuote(in string) string { 44*8fb009dcSAndroid Build Coastguard Worker // See https://cmake.org/cmake/help/v3.0/manual/cmake-language.7.html#quoted-argument 45*8fb009dcSAndroid Build Coastguard Worker var b strings.Builder 46*8fb009dcSAndroid Build Coastguard Worker b.Grow(len(in)) 47*8fb009dcSAndroid Build Coastguard Worker // Iterate over in as bytes. 48*8fb009dcSAndroid Build Coastguard Worker for i := 0; i < len(in); i++ { 49*8fb009dcSAndroid Build Coastguard Worker switch c := in[i]; c { 50*8fb009dcSAndroid Build Coastguard Worker case '\\', '"': 51*8fb009dcSAndroid Build Coastguard Worker b.WriteByte('\\') 52*8fb009dcSAndroid Build Coastguard Worker b.WriteByte(c) 53*8fb009dcSAndroid Build Coastguard Worker case '\t': 54*8fb009dcSAndroid Build Coastguard Worker b.WriteString("\\t") 55*8fb009dcSAndroid Build Coastguard Worker case '\r': 56*8fb009dcSAndroid Build Coastguard Worker b.WriteString("\\r") 57*8fb009dcSAndroid Build Coastguard Worker case '\n': 58*8fb009dcSAndroid Build Coastguard Worker b.WriteString("\\n") 59*8fb009dcSAndroid Build Coastguard Worker default: 60*8fb009dcSAndroid Build Coastguard Worker b.WriteByte(in[i]) 61*8fb009dcSAndroid Build Coastguard Worker } 62*8fb009dcSAndroid Build Coastguard Worker } 63*8fb009dcSAndroid Build Coastguard Worker return b.String() 64*8fb009dcSAndroid Build Coastguard Worker} 65*8fb009dcSAndroid Build Coastguard Worker 66*8fb009dcSAndroid Build Coastguard Workerfunc writeCMake(outFile *os.File, files []string) error { 67*8fb009dcSAndroid Build Coastguard Worker for i, file := range files { 68*8fb009dcSAndroid Build Coastguard Worker if i != 0 { 69*8fb009dcSAndroid Build Coastguard Worker if _, err := outFile.WriteString(";"); err != nil { 70*8fb009dcSAndroid Build Coastguard Worker return err 71*8fb009dcSAndroid Build Coastguard Worker } 72*8fb009dcSAndroid Build Coastguard Worker } 73*8fb009dcSAndroid Build Coastguard Worker if _, err := outFile.WriteString(cMakeQuote(file)); err != nil { 74*8fb009dcSAndroid Build Coastguard Worker return err 75*8fb009dcSAndroid Build Coastguard Worker } 76*8fb009dcSAndroid Build Coastguard Worker } 77*8fb009dcSAndroid Build Coastguard Worker return nil 78*8fb009dcSAndroid Build Coastguard Worker} 79*8fb009dcSAndroid Build Coastguard Worker 80*8fb009dcSAndroid Build Coastguard Workerfunc makeQuote(in string) string { 81*8fb009dcSAndroid Build Coastguard Worker // See https://www.gnu.org/software/make/manual/make.html#Rule-Syntax 82*8fb009dcSAndroid Build Coastguard Worker var b strings.Builder 83*8fb009dcSAndroid Build Coastguard Worker b.Grow(len(in)) 84*8fb009dcSAndroid Build Coastguard Worker // Iterate over in as bytes. 85*8fb009dcSAndroid Build Coastguard Worker for i := 0; i < len(in); i++ { 86*8fb009dcSAndroid Build Coastguard Worker switch c := in[i]; c { 87*8fb009dcSAndroid Build Coastguard Worker case '$': 88*8fb009dcSAndroid Build Coastguard Worker b.WriteString("$$") 89*8fb009dcSAndroid Build Coastguard Worker case '#', '\\', ' ': 90*8fb009dcSAndroid Build Coastguard Worker b.WriteByte('\\') 91*8fb009dcSAndroid Build Coastguard Worker b.WriteByte(c) 92*8fb009dcSAndroid Build Coastguard Worker default: 93*8fb009dcSAndroid Build Coastguard Worker b.WriteByte(c) 94*8fb009dcSAndroid Build Coastguard Worker } 95*8fb009dcSAndroid Build Coastguard Worker } 96*8fb009dcSAndroid Build Coastguard Worker return b.String() 97*8fb009dcSAndroid Build Coastguard Worker} 98*8fb009dcSAndroid Build Coastguard Worker 99*8fb009dcSAndroid Build Coastguard Workerfunc writeDepfile(outFile *os.File, files []string) error { 100*8fb009dcSAndroid Build Coastguard Worker if _, err := fmt.Fprintf(outFile, "%s:", makeQuote(*target)); err != nil { 101*8fb009dcSAndroid Build Coastguard Worker return err 102*8fb009dcSAndroid Build Coastguard Worker } 103*8fb009dcSAndroid Build Coastguard Worker for _, file := range files { 104*8fb009dcSAndroid Build Coastguard Worker if _, err := fmt.Fprintf(outFile, " %s", makeQuote(file)); err != nil { 105*8fb009dcSAndroid Build Coastguard Worker return err 106*8fb009dcSAndroid Build Coastguard Worker } 107*8fb009dcSAndroid Build Coastguard Worker } 108*8fb009dcSAndroid Build Coastguard Worker _, err := outFile.WriteString("\n") 109*8fb009dcSAndroid Build Coastguard Worker return err 110*8fb009dcSAndroid Build Coastguard Worker} 111*8fb009dcSAndroid Build Coastguard Worker 112*8fb009dcSAndroid Build Coastguard Workerfunc appendPrefixed(list, newFiles []string, prefix string) []string { 113*8fb009dcSAndroid Build Coastguard Worker for _, file := range newFiles { 114*8fb009dcSAndroid Build Coastguard Worker list = append(list, filepath.Join(prefix, file)) 115*8fb009dcSAndroid Build Coastguard Worker } 116*8fb009dcSAndroid Build Coastguard Worker return list 117*8fb009dcSAndroid Build Coastguard Worker} 118*8fb009dcSAndroid Build Coastguard Worker 119*8fb009dcSAndroid Build Coastguard Workerfunc main() { 120*8fb009dcSAndroid Build Coastguard Worker flag.Parse() 121*8fb009dcSAndroid Build Coastguard Worker 122*8fb009dcSAndroid Build Coastguard Worker if len(*mainPkg) == 0 { 123*8fb009dcSAndroid Build Coastguard Worker fmt.Fprintf(os.Stderr, "-pkg argument is required.\n") 124*8fb009dcSAndroid Build Coastguard Worker os.Exit(1) 125*8fb009dcSAndroid Build Coastguard Worker } 126*8fb009dcSAndroid Build Coastguard Worker 127*8fb009dcSAndroid Build Coastguard Worker var isDepfile bool 128*8fb009dcSAndroid Build Coastguard Worker switch *format { 129*8fb009dcSAndroid Build Coastguard Worker case "depfile": 130*8fb009dcSAndroid Build Coastguard Worker isDepfile = true 131*8fb009dcSAndroid Build Coastguard Worker case "cmake": 132*8fb009dcSAndroid Build Coastguard Worker isDepfile = false 133*8fb009dcSAndroid Build Coastguard Worker default: 134*8fb009dcSAndroid Build Coastguard Worker fmt.Fprintf(os.Stderr, "Unknown format: %q\n", *format) 135*8fb009dcSAndroid Build Coastguard Worker os.Exit(1) 136*8fb009dcSAndroid Build Coastguard Worker } 137*8fb009dcSAndroid Build Coastguard Worker 138*8fb009dcSAndroid Build Coastguard Worker if isDepfile && len(*target) == 0 { 139*8fb009dcSAndroid Build Coastguard Worker fmt.Fprintf(os.Stderr, "-target argument is required for depfile.\n") 140*8fb009dcSAndroid Build Coastguard Worker os.Exit(1) 141*8fb009dcSAndroid Build Coastguard Worker } 142*8fb009dcSAndroid Build Coastguard Worker 143*8fb009dcSAndroid Build Coastguard Worker done := make(map[string]struct{}) 144*8fb009dcSAndroid Build Coastguard Worker var files []string 145*8fb009dcSAndroid Build Coastguard Worker var recurse func(pkgName string) error 146*8fb009dcSAndroid Build Coastguard Worker recurse = func(pkgName string) error { 147*8fb009dcSAndroid Build Coastguard Worker pkg, err := build.Default.Import(pkgName, ".", 0) 148*8fb009dcSAndroid Build Coastguard Worker if err != nil { 149*8fb009dcSAndroid Build Coastguard Worker return err 150*8fb009dcSAndroid Build Coastguard Worker } 151*8fb009dcSAndroid Build Coastguard Worker 152*8fb009dcSAndroid Build Coastguard Worker // Skip standard packages. 153*8fb009dcSAndroid Build Coastguard Worker if pkg.Goroot { 154*8fb009dcSAndroid Build Coastguard Worker return nil 155*8fb009dcSAndroid Build Coastguard Worker } 156*8fb009dcSAndroid Build Coastguard Worker 157*8fb009dcSAndroid Build Coastguard Worker // Skip already-visited packages. 158*8fb009dcSAndroid Build Coastguard Worker if _, ok := done[pkg.Dir]; ok { 159*8fb009dcSAndroid Build Coastguard Worker return nil 160*8fb009dcSAndroid Build Coastguard Worker } 161*8fb009dcSAndroid Build Coastguard Worker done[pkg.Dir] = struct{}{} 162*8fb009dcSAndroid Build Coastguard Worker 163*8fb009dcSAndroid Build Coastguard Worker files = appendPrefixed(files, pkg.GoFiles, pkg.Dir) 164*8fb009dcSAndroid Build Coastguard Worker files = appendPrefixed(files, pkg.CgoFiles, pkg.Dir) 165*8fb009dcSAndroid Build Coastguard Worker // Include ignored Go files. A subsequent change may cause them 166*8fb009dcSAndroid Build Coastguard Worker // to no longer be ignored. 167*8fb009dcSAndroid Build Coastguard Worker files = appendPrefixed(files, pkg.IgnoredGoFiles, pkg.Dir) 168*8fb009dcSAndroid Build Coastguard Worker 169*8fb009dcSAndroid Build Coastguard Worker // Recurse into imports. 170*8fb009dcSAndroid Build Coastguard Worker for _, importName := range pkg.Imports { 171*8fb009dcSAndroid Build Coastguard Worker if err := recurse(importName); err != nil { 172*8fb009dcSAndroid Build Coastguard Worker return err 173*8fb009dcSAndroid Build Coastguard Worker } 174*8fb009dcSAndroid Build Coastguard Worker } 175*8fb009dcSAndroid Build Coastguard Worker return nil 176*8fb009dcSAndroid Build Coastguard Worker } 177*8fb009dcSAndroid Build Coastguard Worker if err := recurse(*mainPkg); err != nil { 178*8fb009dcSAndroid Build Coastguard Worker fmt.Fprintf(os.Stderr, "Error getting dependencies: %s\n", err) 179*8fb009dcSAndroid Build Coastguard Worker os.Exit(1) 180*8fb009dcSAndroid Build Coastguard Worker } 181*8fb009dcSAndroid Build Coastguard Worker 182*8fb009dcSAndroid Build Coastguard Worker sort.Strings(files) 183*8fb009dcSAndroid Build Coastguard Worker 184*8fb009dcSAndroid Build Coastguard Worker outFile := os.Stdout 185*8fb009dcSAndroid Build Coastguard Worker if len(*out) != 0 { 186*8fb009dcSAndroid Build Coastguard Worker var err error 187*8fb009dcSAndroid Build Coastguard Worker outFile, err = os.Create(*out) 188*8fb009dcSAndroid Build Coastguard Worker if err != nil { 189*8fb009dcSAndroid Build Coastguard Worker fmt.Fprintf(os.Stderr, "Error writing output: %s\n", err) 190*8fb009dcSAndroid Build Coastguard Worker os.Exit(1) 191*8fb009dcSAndroid Build Coastguard Worker } 192*8fb009dcSAndroid Build Coastguard Worker defer outFile.Close() 193*8fb009dcSAndroid Build Coastguard Worker } 194*8fb009dcSAndroid Build Coastguard Worker 195*8fb009dcSAndroid Build Coastguard Worker var err error 196*8fb009dcSAndroid Build Coastguard Worker if isDepfile { 197*8fb009dcSAndroid Build Coastguard Worker err = writeDepfile(outFile, files) 198*8fb009dcSAndroid Build Coastguard Worker } else { 199*8fb009dcSAndroid Build Coastguard Worker err = writeCMake(outFile, files) 200*8fb009dcSAndroid Build Coastguard Worker } 201*8fb009dcSAndroid Build Coastguard Worker if err != nil { 202*8fb009dcSAndroid Build Coastguard Worker fmt.Fprintf(os.Stderr, "Error writing output: %s\n", err) 203*8fb009dcSAndroid Build Coastguard Worker os.Exit(1) 204*8fb009dcSAndroid Build Coastguard Worker } 205*8fb009dcSAndroid Build Coastguard Worker} 206