xref: /aosp_15_r20/external/bazelbuild-rules_python/gazelle/manifest/generate/generate.go (revision 60517a1edbc8ecf509223e9af94a7adec7d736b8)
1*60517a1eSAndroid Build Coastguard Worker// Copyright 2023 The Bazel Authors. All rights reserved.
2*60517a1eSAndroid Build Coastguard Worker//
3*60517a1eSAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License");
4*60517a1eSAndroid Build Coastguard Worker// you may not use this file except in compliance with the License.
5*60517a1eSAndroid Build Coastguard Worker// You may obtain a copy of the License at
6*60517a1eSAndroid Build Coastguard Worker//
7*60517a1eSAndroid Build Coastguard Worker//     http://www.apache.org/licenses/LICENSE-2.0
8*60517a1eSAndroid Build Coastguard Worker//
9*60517a1eSAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software
10*60517a1eSAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS,
11*60517a1eSAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*60517a1eSAndroid Build Coastguard Worker// See the License for the specific language governing permissions and
13*60517a1eSAndroid Build Coastguard Worker// limitations under the License.
14*60517a1eSAndroid Build Coastguard Worker
15*60517a1eSAndroid Build Coastguard Worker/*
16*60517a1eSAndroid Build Coastguard Workergenerate.go is a program that generates the Gazelle YAML manifest.
17*60517a1eSAndroid Build Coastguard Worker
18*60517a1eSAndroid Build Coastguard WorkerThe Gazelle manifest is a file that contains extra information required when
19*60517a1eSAndroid Build Coastguard Workergenerating the Bazel BUILD files.
20*60517a1eSAndroid Build Coastguard Worker*/
21*60517a1eSAndroid Build Coastguard Workerpackage main
22*60517a1eSAndroid Build Coastguard Worker
23*60517a1eSAndroid Build Coastguard Workerimport (
24*60517a1eSAndroid Build Coastguard Worker	"encoding/json"
25*60517a1eSAndroid Build Coastguard Worker	"flag"
26*60517a1eSAndroid Build Coastguard Worker	"fmt"
27*60517a1eSAndroid Build Coastguard Worker	"log"
28*60517a1eSAndroid Build Coastguard Worker	"os"
29*60517a1eSAndroid Build Coastguard Worker	"strings"
30*60517a1eSAndroid Build Coastguard Worker
31*60517a1eSAndroid Build Coastguard Worker	"github.com/bazelbuild/rules_python/gazelle/manifest"
32*60517a1eSAndroid Build Coastguard Worker)
33*60517a1eSAndroid Build Coastguard Worker
34*60517a1eSAndroid Build Coastguard Workerfunc main() {
35*60517a1eSAndroid Build Coastguard Worker	var (
36*60517a1eSAndroid Build Coastguard Worker		manifestGeneratorHashPath string
37*60517a1eSAndroid Build Coastguard Worker		requirementsPath          string
38*60517a1eSAndroid Build Coastguard Worker		pipRepositoryName         string
39*60517a1eSAndroid Build Coastguard Worker		modulesMappingPath        string
40*60517a1eSAndroid Build Coastguard Worker		outputPath                string
41*60517a1eSAndroid Build Coastguard Worker		updateTarget              string
42*60517a1eSAndroid Build Coastguard Worker	)
43*60517a1eSAndroid Build Coastguard Worker	flag.StringVar(
44*60517a1eSAndroid Build Coastguard Worker		&manifestGeneratorHashPath,
45*60517a1eSAndroid Build Coastguard Worker		"manifest-generator-hash",
46*60517a1eSAndroid Build Coastguard Worker		"",
47*60517a1eSAndroid Build Coastguard Worker		"The file containing the hash for the source code of the manifest generator."+
48*60517a1eSAndroid Build Coastguard Worker			"This is important to force manifest updates when the generator logic changes.")
49*60517a1eSAndroid Build Coastguard Worker	flag.StringVar(
50*60517a1eSAndroid Build Coastguard Worker		&requirementsPath,
51*60517a1eSAndroid Build Coastguard Worker		"requirements",
52*60517a1eSAndroid Build Coastguard Worker		"",
53*60517a1eSAndroid Build Coastguard Worker		"The requirements.txt file.")
54*60517a1eSAndroid Build Coastguard Worker	flag.StringVar(
55*60517a1eSAndroid Build Coastguard Worker		&pipRepositoryName,
56*60517a1eSAndroid Build Coastguard Worker		"pip-repository-name",
57*60517a1eSAndroid Build Coastguard Worker		"",
58*60517a1eSAndroid Build Coastguard Worker		"The name of the pip_install or pip_repository target.")
59*60517a1eSAndroid Build Coastguard Worker	flag.StringVar(
60*60517a1eSAndroid Build Coastguard Worker		&modulesMappingPath,
61*60517a1eSAndroid Build Coastguard Worker		"modules-mapping",
62*60517a1eSAndroid Build Coastguard Worker		"",
63*60517a1eSAndroid Build Coastguard Worker		"The modules_mapping.json file.")
64*60517a1eSAndroid Build Coastguard Worker	flag.StringVar(
65*60517a1eSAndroid Build Coastguard Worker		&outputPath,
66*60517a1eSAndroid Build Coastguard Worker		"output",
67*60517a1eSAndroid Build Coastguard Worker		"",
68*60517a1eSAndroid Build Coastguard Worker		"The output YAML manifest file.")
69*60517a1eSAndroid Build Coastguard Worker	flag.StringVar(
70*60517a1eSAndroid Build Coastguard Worker		&updateTarget,
71*60517a1eSAndroid Build Coastguard Worker		"update-target",
72*60517a1eSAndroid Build Coastguard Worker		"",
73*60517a1eSAndroid Build Coastguard Worker		"The Bazel target to update the YAML manifest file.")
74*60517a1eSAndroid Build Coastguard Worker	flag.Parse()
75*60517a1eSAndroid Build Coastguard Worker
76*60517a1eSAndroid Build Coastguard Worker	if modulesMappingPath == "" {
77*60517a1eSAndroid Build Coastguard Worker		log.Fatalln("ERROR: --modules-mapping must be set")
78*60517a1eSAndroid Build Coastguard Worker	}
79*60517a1eSAndroid Build Coastguard Worker
80*60517a1eSAndroid Build Coastguard Worker	if outputPath == "" {
81*60517a1eSAndroid Build Coastguard Worker		log.Fatalln("ERROR: --output must be set")
82*60517a1eSAndroid Build Coastguard Worker	}
83*60517a1eSAndroid Build Coastguard Worker
84*60517a1eSAndroid Build Coastguard Worker	if updateTarget == "" {
85*60517a1eSAndroid Build Coastguard Worker		log.Fatalln("ERROR: --update-target must be set")
86*60517a1eSAndroid Build Coastguard Worker	}
87*60517a1eSAndroid Build Coastguard Worker
88*60517a1eSAndroid Build Coastguard Worker	modulesMapping, err := unmarshalJSON(modulesMappingPath)
89*60517a1eSAndroid Build Coastguard Worker	if err != nil {
90*60517a1eSAndroid Build Coastguard Worker		log.Fatalf("ERROR: %v\n", err)
91*60517a1eSAndroid Build Coastguard Worker	}
92*60517a1eSAndroid Build Coastguard Worker
93*60517a1eSAndroid Build Coastguard Worker	header := generateHeader(updateTarget)
94*60517a1eSAndroid Build Coastguard Worker	repository := manifest.PipRepository{
95*60517a1eSAndroid Build Coastguard Worker		Name: pipRepositoryName,
96*60517a1eSAndroid Build Coastguard Worker	}
97*60517a1eSAndroid Build Coastguard Worker
98*60517a1eSAndroid Build Coastguard Worker	manifestFile := manifest.NewFile(&manifest.Manifest{
99*60517a1eSAndroid Build Coastguard Worker		ModulesMapping: modulesMapping,
100*60517a1eSAndroid Build Coastguard Worker		PipRepository:  &repository,
101*60517a1eSAndroid Build Coastguard Worker	})
102*60517a1eSAndroid Build Coastguard Worker	if err := writeOutput(
103*60517a1eSAndroid Build Coastguard Worker		outputPath,
104*60517a1eSAndroid Build Coastguard Worker		header,
105*60517a1eSAndroid Build Coastguard Worker		manifestFile,
106*60517a1eSAndroid Build Coastguard Worker		manifestGeneratorHashPath,
107*60517a1eSAndroid Build Coastguard Worker		requirementsPath,
108*60517a1eSAndroid Build Coastguard Worker	); err != nil {
109*60517a1eSAndroid Build Coastguard Worker		log.Fatalf("ERROR: %v\n", err)
110*60517a1eSAndroid Build Coastguard Worker	}
111*60517a1eSAndroid Build Coastguard Worker}
112*60517a1eSAndroid Build Coastguard Worker
113*60517a1eSAndroid Build Coastguard Worker// unmarshalJSON returns the parsed mapping from the given JSON file path.
114*60517a1eSAndroid Build Coastguard Workerfunc unmarshalJSON(jsonPath string) (map[string]string, error) {
115*60517a1eSAndroid Build Coastguard Worker	file, err := os.Open(jsonPath)
116*60517a1eSAndroid Build Coastguard Worker	if err != nil {
117*60517a1eSAndroid Build Coastguard Worker		return nil, fmt.Errorf("failed to unmarshal JSON file: %w", err)
118*60517a1eSAndroid Build Coastguard Worker	}
119*60517a1eSAndroid Build Coastguard Worker	defer file.Close()
120*60517a1eSAndroid Build Coastguard Worker
121*60517a1eSAndroid Build Coastguard Worker	decoder := json.NewDecoder(file)
122*60517a1eSAndroid Build Coastguard Worker	output := make(map[string]string)
123*60517a1eSAndroid Build Coastguard Worker	if err := decoder.Decode(&output); err != nil {
124*60517a1eSAndroid Build Coastguard Worker		return nil, fmt.Errorf("failed to unmarshal JSON file: %w", err)
125*60517a1eSAndroid Build Coastguard Worker	}
126*60517a1eSAndroid Build Coastguard Worker
127*60517a1eSAndroid Build Coastguard Worker	return output, nil
128*60517a1eSAndroid Build Coastguard Worker}
129*60517a1eSAndroid Build Coastguard Worker
130*60517a1eSAndroid Build Coastguard Worker// generateHeader generates the YAML header human-readable comment.
131*60517a1eSAndroid Build Coastguard Workerfunc generateHeader(updateTarget string) string {
132*60517a1eSAndroid Build Coastguard Worker	var header strings.Builder
133*60517a1eSAndroid Build Coastguard Worker	header.WriteString("# GENERATED FILE - DO NOT EDIT!\n")
134*60517a1eSAndroid Build Coastguard Worker	header.WriteString("#\n")
135*60517a1eSAndroid Build Coastguard Worker	header.WriteString("# To update this file, run:\n")
136*60517a1eSAndroid Build Coastguard Worker	header.WriteString(fmt.Sprintf("#   bazel run %s\n", updateTarget))
137*60517a1eSAndroid Build Coastguard Worker	return header.String()
138*60517a1eSAndroid Build Coastguard Worker}
139*60517a1eSAndroid Build Coastguard Worker
140*60517a1eSAndroid Build Coastguard Worker// writeOutput writes to the final file the header and manifest structure.
141*60517a1eSAndroid Build Coastguard Workerfunc writeOutput(
142*60517a1eSAndroid Build Coastguard Worker	outputPath string,
143*60517a1eSAndroid Build Coastguard Worker	header string,
144*60517a1eSAndroid Build Coastguard Worker	manifestFile *manifest.File,
145*60517a1eSAndroid Build Coastguard Worker	manifestGeneratorHashPath string,
146*60517a1eSAndroid Build Coastguard Worker	requirementsPath string,
147*60517a1eSAndroid Build Coastguard Worker) error {
148*60517a1eSAndroid Build Coastguard Worker	outputFile, err := os.OpenFile(outputPath, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644)
149*60517a1eSAndroid Build Coastguard Worker	if err != nil {
150*60517a1eSAndroid Build Coastguard Worker		return fmt.Errorf("failed to write output: %w", err)
151*60517a1eSAndroid Build Coastguard Worker	}
152*60517a1eSAndroid Build Coastguard Worker	defer outputFile.Close()
153*60517a1eSAndroid Build Coastguard Worker
154*60517a1eSAndroid Build Coastguard Worker	if _, err := fmt.Fprintf(outputFile, "%s\n", header); err != nil {
155*60517a1eSAndroid Build Coastguard Worker		return fmt.Errorf("failed to write output: %w", err)
156*60517a1eSAndroid Build Coastguard Worker	}
157*60517a1eSAndroid Build Coastguard Worker
158*60517a1eSAndroid Build Coastguard Worker	if requirementsPath != "" {
159*60517a1eSAndroid Build Coastguard Worker		manifestGeneratorHash, err := os.Open(manifestGeneratorHashPath)
160*60517a1eSAndroid Build Coastguard Worker		if err != nil {
161*60517a1eSAndroid Build Coastguard Worker			return fmt.Errorf("failed to write output: %w", err)
162*60517a1eSAndroid Build Coastguard Worker		}
163*60517a1eSAndroid Build Coastguard Worker		defer manifestGeneratorHash.Close()
164*60517a1eSAndroid Build Coastguard Worker
165*60517a1eSAndroid Build Coastguard Worker		requirements, err := os.Open(requirementsPath)
166*60517a1eSAndroid Build Coastguard Worker		if err != nil {
167*60517a1eSAndroid Build Coastguard Worker			return fmt.Errorf("failed to write output: %w", err)
168*60517a1eSAndroid Build Coastguard Worker		}
169*60517a1eSAndroid Build Coastguard Worker		defer requirements.Close()
170*60517a1eSAndroid Build Coastguard Worker
171*60517a1eSAndroid Build Coastguard Worker		if err := manifestFile.EncodeWithIntegrity(outputFile, manifestGeneratorHash, requirements); err != nil {
172*60517a1eSAndroid Build Coastguard Worker			return fmt.Errorf("failed to write output: %w", err)
173*60517a1eSAndroid Build Coastguard Worker		}
174*60517a1eSAndroid Build Coastguard Worker	} else {
175*60517a1eSAndroid Build Coastguard Worker		if err := manifestFile.EncodeWithoutIntegrity(outputFile); err != nil {
176*60517a1eSAndroid Build Coastguard Worker			return fmt.Errorf("failed to write output: %w", err)
177*60517a1eSAndroid Build Coastguard Worker		}
178*60517a1eSAndroid Build Coastguard Worker	}
179*60517a1eSAndroid Build Coastguard Worker
180*60517a1eSAndroid Build Coastguard Worker	return nil
181*60517a1eSAndroid Build Coastguard Worker}
182