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