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