xref: /aosp_15_r20/build/make/tools/compliance/cmd/checkmetadata/checkmetadata.go (revision 9e94795a3d4ef5c1d47486f9a02bb378756cea8a)
1*9e94795aSAndroid Build Coastguard Worker// Copyright 2022 Google LLC
2*9e94795aSAndroid Build Coastguard Worker//
3*9e94795aSAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License");
4*9e94795aSAndroid Build Coastguard Worker// you may not use this file except in compliance with the License.
5*9e94795aSAndroid Build Coastguard Worker// You may obtain a copy of the License at
6*9e94795aSAndroid Build Coastguard Worker//
7*9e94795aSAndroid Build Coastguard Worker//      http://www.apache.org/licenses/LICENSE-2.0
8*9e94795aSAndroid Build Coastguard Worker//
9*9e94795aSAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software
10*9e94795aSAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS,
11*9e94795aSAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*9e94795aSAndroid Build Coastguard Worker// See the License for the specific language governing permissions and
13*9e94795aSAndroid Build Coastguard Worker// limitations under the License.
14*9e94795aSAndroid Build Coastguard Worker
15*9e94795aSAndroid Build Coastguard Workerpackage main
16*9e94795aSAndroid Build Coastguard Worker
17*9e94795aSAndroid Build Coastguard Workerimport (
18*9e94795aSAndroid Build Coastguard Worker	"bytes"
19*9e94795aSAndroid Build Coastguard Worker	"flag"
20*9e94795aSAndroid Build Coastguard Worker	"fmt"
21*9e94795aSAndroid Build Coastguard Worker	"io"
22*9e94795aSAndroid Build Coastguard Worker	"io/fs"
23*9e94795aSAndroid Build Coastguard Worker	"os"
24*9e94795aSAndroid Build Coastguard Worker	"path/filepath"
25*9e94795aSAndroid Build Coastguard Worker	"strings"
26*9e94795aSAndroid Build Coastguard Worker
27*9e94795aSAndroid Build Coastguard Worker	"android/soong/response"
28*9e94795aSAndroid Build Coastguard Worker	"android/soong/tools/compliance"
29*9e94795aSAndroid Build Coastguard Worker	"android/soong/tools/compliance/projectmetadata"
30*9e94795aSAndroid Build Coastguard Worker)
31*9e94795aSAndroid Build Coastguard Worker
32*9e94795aSAndroid Build Coastguard Workervar (
33*9e94795aSAndroid Build Coastguard Worker	failNoneRequested = fmt.Errorf("\nNo projects requested")
34*9e94795aSAndroid Build Coastguard Worker)
35*9e94795aSAndroid Build Coastguard Worker
36*9e94795aSAndroid Build Coastguard Workerfunc main() {
37*9e94795aSAndroid Build Coastguard Worker	var expandedArgs []string
38*9e94795aSAndroid Build Coastguard Worker	for _, arg := range os.Args[1:] {
39*9e94795aSAndroid Build Coastguard Worker		if strings.HasPrefix(arg, "@") {
40*9e94795aSAndroid Build Coastguard Worker			f, err := os.Open(strings.TrimPrefix(arg, "@"))
41*9e94795aSAndroid Build Coastguard Worker			if err != nil {
42*9e94795aSAndroid Build Coastguard Worker				fmt.Fprintln(os.Stderr, err.Error())
43*9e94795aSAndroid Build Coastguard Worker				os.Exit(1)
44*9e94795aSAndroid Build Coastguard Worker			}
45*9e94795aSAndroid Build Coastguard Worker
46*9e94795aSAndroid Build Coastguard Worker			respArgs, err := response.ReadRspFile(f)
47*9e94795aSAndroid Build Coastguard Worker			f.Close()
48*9e94795aSAndroid Build Coastguard Worker			if err != nil {
49*9e94795aSAndroid Build Coastguard Worker				fmt.Fprintln(os.Stderr, err.Error())
50*9e94795aSAndroid Build Coastguard Worker				os.Exit(1)
51*9e94795aSAndroid Build Coastguard Worker			}
52*9e94795aSAndroid Build Coastguard Worker			expandedArgs = append(expandedArgs, respArgs...)
53*9e94795aSAndroid Build Coastguard Worker		} else {
54*9e94795aSAndroid Build Coastguard Worker			expandedArgs = append(expandedArgs, arg)
55*9e94795aSAndroid Build Coastguard Worker		}
56*9e94795aSAndroid Build Coastguard Worker	}
57*9e94795aSAndroid Build Coastguard Worker
58*9e94795aSAndroid Build Coastguard Worker	flags := flag.NewFlagSet("flags", flag.ExitOnError)
59*9e94795aSAndroid Build Coastguard Worker
60*9e94795aSAndroid Build Coastguard Worker	flags.Usage = func() {
61*9e94795aSAndroid Build Coastguard Worker		fmt.Fprintf(os.Stderr, `Usage: %s {-o outfile} projectdir {projectdir...}
62*9e94795aSAndroid Build Coastguard Worker
63*9e94795aSAndroid Build Coastguard WorkerTries to open the METADATA.android or METADATA file in each projectdir
64*9e94795aSAndroid Build Coastguard Workerreporting any errors on stderr.
65*9e94795aSAndroid Build Coastguard Worker
66*9e94795aSAndroid Build Coastguard WorkerReports "FAIL" to stdout if any errors found and exits with status 1.
67*9e94795aSAndroid Build Coastguard Worker
68*9e94795aSAndroid Build Coastguard WorkerOtherwise, reports "PASS" and the number of project metadata files
69*9e94795aSAndroid Build Coastguard Workerfound exiting with status 0.
70*9e94795aSAndroid Build Coastguard Worker`, filepath.Base(os.Args[0]))
71*9e94795aSAndroid Build Coastguard Worker		flags.PrintDefaults()
72*9e94795aSAndroid Build Coastguard Worker	}
73*9e94795aSAndroid Build Coastguard Worker
74*9e94795aSAndroid Build Coastguard Worker	outputFile := flags.String("o", "-", "Where to write the output. (default stdout)")
75*9e94795aSAndroid Build Coastguard Worker
76*9e94795aSAndroid Build Coastguard Worker	flags.Parse(expandedArgs)
77*9e94795aSAndroid Build Coastguard Worker
78*9e94795aSAndroid Build Coastguard Worker	// Must specify at least one root target.
79*9e94795aSAndroid Build Coastguard Worker	if flags.NArg() == 0 {
80*9e94795aSAndroid Build Coastguard Worker		flags.Usage()
81*9e94795aSAndroid Build Coastguard Worker		os.Exit(2)
82*9e94795aSAndroid Build Coastguard Worker	}
83*9e94795aSAndroid Build Coastguard Worker
84*9e94795aSAndroid Build Coastguard Worker	if len(*outputFile) == 0 {
85*9e94795aSAndroid Build Coastguard Worker		flags.Usage()
86*9e94795aSAndroid Build Coastguard Worker		fmt.Fprintf(os.Stderr, "must specify file for -o; use - for stdout\n")
87*9e94795aSAndroid Build Coastguard Worker		os.Exit(2)
88*9e94795aSAndroid Build Coastguard Worker	} else {
89*9e94795aSAndroid Build Coastguard Worker		dir, err := filepath.Abs(filepath.Dir(*outputFile))
90*9e94795aSAndroid Build Coastguard Worker		if err != nil {
91*9e94795aSAndroid Build Coastguard Worker			fmt.Fprintf(os.Stderr, "cannot determine path to %q: %s\n", *outputFile, err)
92*9e94795aSAndroid Build Coastguard Worker			os.Exit(1)
93*9e94795aSAndroid Build Coastguard Worker		}
94*9e94795aSAndroid Build Coastguard Worker		fi, err := os.Stat(dir)
95*9e94795aSAndroid Build Coastguard Worker		if err != nil {
96*9e94795aSAndroid Build Coastguard Worker			fmt.Fprintf(os.Stderr, "cannot read directory %q of %q: %s\n", dir, *outputFile, err)
97*9e94795aSAndroid Build Coastguard Worker			os.Exit(1)
98*9e94795aSAndroid Build Coastguard Worker		}
99*9e94795aSAndroid Build Coastguard Worker		if !fi.IsDir() {
100*9e94795aSAndroid Build Coastguard Worker			fmt.Fprintf(os.Stderr, "parent %q of %q is not a directory\n", dir, *outputFile)
101*9e94795aSAndroid Build Coastguard Worker			os.Exit(1)
102*9e94795aSAndroid Build Coastguard Worker		}
103*9e94795aSAndroid Build Coastguard Worker	}
104*9e94795aSAndroid Build Coastguard Worker
105*9e94795aSAndroid Build Coastguard Worker	var ofile io.Writer
106*9e94795aSAndroid Build Coastguard Worker	ofile = os.Stdout
107*9e94795aSAndroid Build Coastguard Worker	var obuf *bytes.Buffer
108*9e94795aSAndroid Build Coastguard Worker	if *outputFile != "-" {
109*9e94795aSAndroid Build Coastguard Worker		obuf = &bytes.Buffer{}
110*9e94795aSAndroid Build Coastguard Worker		ofile = obuf
111*9e94795aSAndroid Build Coastguard Worker	}
112*9e94795aSAndroid Build Coastguard Worker
113*9e94795aSAndroid Build Coastguard Worker	err := checkProjectMetadata(ofile, os.Stderr, compliance.FS, flags.Args()...)
114*9e94795aSAndroid Build Coastguard Worker	if err != nil {
115*9e94795aSAndroid Build Coastguard Worker		if err == failNoneRequested {
116*9e94795aSAndroid Build Coastguard Worker			flags.Usage()
117*9e94795aSAndroid Build Coastguard Worker		}
118*9e94795aSAndroid Build Coastguard Worker		fmt.Fprintf(os.Stderr, "%s\n", err.Error())
119*9e94795aSAndroid Build Coastguard Worker		fmt.Fprintln(ofile, "FAIL")
120*9e94795aSAndroid Build Coastguard Worker		os.Exit(1)
121*9e94795aSAndroid Build Coastguard Worker	}
122*9e94795aSAndroid Build Coastguard Worker	if *outputFile != "-" {
123*9e94795aSAndroid Build Coastguard Worker		err := os.WriteFile(*outputFile, obuf.Bytes(), 0666)
124*9e94795aSAndroid Build Coastguard Worker		if err != nil {
125*9e94795aSAndroid Build Coastguard Worker			fmt.Fprintf(os.Stderr, "could not write output to %q from %q: %s\n", *outputFile, os.Getenv("PWD"), err)
126*9e94795aSAndroid Build Coastguard Worker			os.Exit(1)
127*9e94795aSAndroid Build Coastguard Worker		}
128*9e94795aSAndroid Build Coastguard Worker	}
129*9e94795aSAndroid Build Coastguard Worker	os.Exit(0)
130*9e94795aSAndroid Build Coastguard Worker}
131*9e94795aSAndroid Build Coastguard Worker
132*9e94795aSAndroid Build Coastguard Worker// checkProjectMetadata implements the checkmetadata utility.
133*9e94795aSAndroid Build Coastguard Workerfunc checkProjectMetadata(stdout, stderr io.Writer, rootFS fs.FS, projects ...string) error {
134*9e94795aSAndroid Build Coastguard Worker
135*9e94795aSAndroid Build Coastguard Worker	if len(projects) < 1 {
136*9e94795aSAndroid Build Coastguard Worker		return failNoneRequested
137*9e94795aSAndroid Build Coastguard Worker	}
138*9e94795aSAndroid Build Coastguard Worker
139*9e94795aSAndroid Build Coastguard Worker	// Read the project metadata files from `projects`
140*9e94795aSAndroid Build Coastguard Worker	ix := projectmetadata.NewIndex(rootFS)
141*9e94795aSAndroid Build Coastguard Worker	pms, err := ix.MetadataForProjects(projects...)
142*9e94795aSAndroid Build Coastguard Worker	if err != nil {
143*9e94795aSAndroid Build Coastguard Worker		return fmt.Errorf("Unable to read project metadata file(s) %q from %q: %w\n", projects, os.Getenv("PWD"), err)
144*9e94795aSAndroid Build Coastguard Worker	}
145*9e94795aSAndroid Build Coastguard Worker
146*9e94795aSAndroid Build Coastguard Worker	fmt.Fprintf(stdout, "PASS -- parsed %d project metadata files for %d projects\n", len(pms), len(projects))
147*9e94795aSAndroid Build Coastguard Worker	return nil
148*9e94795aSAndroid Build Coastguard Worker}
149