264 lines
8.6 KiB
Go
264 lines
8.6 KiB
Go
// Copyright 2022 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
package main
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"regexp"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
type depConfig struct {
|
|
bazelNameOverride string // Bazel style uses underscores not dashes, so we fix those if needed.
|
|
needsBazelFile bool
|
|
}
|
|
|
|
// These are all deps used by the Bazel build. They are a subset of those listed in DEPS.
|
|
// The key is the name of the repo as specified in DEPS.
|
|
var deps = map[string]depConfig{
|
|
"abseil-cpp": {bazelNameOverride: "abseil_cpp"},
|
|
"brotli": {},
|
|
"highway": {},
|
|
"spirv-tools": {bazelNameOverride: "spirv_tools"},
|
|
// This name is important because spirv_tools expects @spirv_headers to exist by that name.
|
|
"spirv-headers": {bazelNameOverride: "spirv_headers"},
|
|
|
|
"dawn": {needsBazelFile: true},
|
|
"dng_sdk": {needsBazelFile: true},
|
|
"expat": {needsBazelFile: true},
|
|
"freetype": {needsBazelFile: true},
|
|
"harfbuzz": {needsBazelFile: true},
|
|
"icu": {needsBazelFile: true},
|
|
"libavif": {needsBazelFile: true},
|
|
"libgav1": {needsBazelFile: true},
|
|
"libjpeg-turbo": {bazelNameOverride: "libjpeg_turbo", needsBazelFile: true},
|
|
"libjxl": {needsBazelFile: true},
|
|
"libpng": {needsBazelFile: true},
|
|
"libwebp": {needsBazelFile: true},
|
|
"libyuv": {needsBazelFile: true},
|
|
"spirv-cross": {bazelNameOverride: "spirv_cross", needsBazelFile: true},
|
|
"perfetto": {needsBazelFile: true},
|
|
"piex": {needsBazelFile: true},
|
|
"vulkan-headers": {bazelNameOverride: "vulkan_headers", needsBazelFile: true},
|
|
"vulkan-tools": {bazelNameOverride: "vulkan_tools", needsBazelFile: true},
|
|
"vulkanmemoryallocator": {needsBazelFile: true},
|
|
"wuffs": {needsBazelFile: true},
|
|
// Some other dependency downloads zlib but with their own rules
|
|
"zlib": {bazelNameOverride: "zlib_skia", needsBazelFile: true},
|
|
}
|
|
|
|
func main() {
|
|
var (
|
|
depsFile = flag.String("deps_file", "DEPS", "The location of the DEPS file. Usually at the root of the repository")
|
|
genBzlFile = flag.String("gen_bzl_file", "bazel/deps.bzl", "The location of the .bzl file that has the generated Bazel repository rules.")
|
|
workspaceFile = flag.String("workspace_file", "WORKSPACE.bazel", "The location of the WORKSPACE file that should be updated with dep names.")
|
|
// https://bazel.build/docs/user-manual#running-executables
|
|
repoDir = flag.String("repo_dir", os.Getenv("BUILD_WORKSPACE_DIRECTORY"), "The root directory of the repo. Default set by BUILD_WORKSPACE_DIRECTORY env variable.")
|
|
)
|
|
flag.Parse()
|
|
|
|
if *repoDir == "" {
|
|
fmt.Println(`Must set --repo_dir
|
|
This is done automatically via:
|
|
bazel run //bazel/deps_parser`)
|
|
os.Exit(1)
|
|
}
|
|
|
|
if *depsFile == "" || *genBzlFile == "" {
|
|
fmt.Println("Must set --deps_file and --gen_bzl_file")
|
|
flag.PrintDefaults()
|
|
}
|
|
|
|
if err := os.Chdir(*repoDir); err != nil {
|
|
fmt.Printf("Could not cd to %s\n", *repoDir)
|
|
os.Exit(1)
|
|
}
|
|
|
|
b, err := os.ReadFile(*depsFile)
|
|
if err != nil {
|
|
fmt.Printf("Could not open %s: %s\n", *depsFile, err)
|
|
os.Exit(1)
|
|
}
|
|
contents := strings.Split(string(b), "\n")
|
|
|
|
outputFile, count, err := parseDEPSFile(contents, *workspaceFile)
|
|
if err := os.Rename(outputFile, *genBzlFile); err != nil {
|
|
fmt.Printf("Could not write from %s to %s: %s\n", outputFile, *depsFile, err)
|
|
os.Exit(1)
|
|
}
|
|
fmt.Printf("Wrote %d deps\n", count)
|
|
}
|
|
|
|
func parseDEPSFile(contents []string, workspaceFile string) (string, int, error) {
|
|
depsLine := regexp.MustCompile(`externals/(\S+)".+"(https.+)@([a-f0-9]+)"`)
|
|
outputFile, err := os.CreateTemp("", "genbzl")
|
|
if err != nil {
|
|
return "", 0, fmt.Errorf("Could not create output file: %s\n", err)
|
|
}
|
|
defer outputFile.Close()
|
|
|
|
if _, err := outputFile.WriteString(header); err != nil {
|
|
return "", 0, fmt.Errorf("Could not write to output file %s: %s\n", outputFile.Name(), err)
|
|
}
|
|
|
|
var nativeRepos []string
|
|
var providedRepos []string
|
|
|
|
count := 0
|
|
for _, line := range contents {
|
|
if match := depsLine.FindStringSubmatch(line); len(match) > 0 {
|
|
id := match[1]
|
|
repo := match[2]
|
|
rev := match[3]
|
|
|
|
cfg, ok := deps[id]
|
|
if !ok {
|
|
continue
|
|
}
|
|
if cfg.bazelNameOverride != "" {
|
|
id = cfg.bazelNameOverride
|
|
}
|
|
if cfg.needsBazelFile {
|
|
if err := writeNewGitRepositoryRule(outputFile, id, repo, rev); err != nil {
|
|
return "", 0, fmt.Errorf("Could not write to output file %s: %s\n", outputFile.Name(), err)
|
|
}
|
|
workspaceLine := fmt.Sprintf("# @%s - //bazel/external/%s:BUILD.bazel", id, id)
|
|
providedRepos = append(providedRepos, workspaceLine)
|
|
} else {
|
|
if err := writeGitRepositoryRule(outputFile, id, repo, rev); err != nil {
|
|
return "", 0, fmt.Errorf("Could not write to output file %s: %s\n", outputFile.Name(), err)
|
|
}
|
|
workspaceLine := fmt.Sprintf("# @%s - %s", id, repo)
|
|
nativeRepos = append(nativeRepos, workspaceLine)
|
|
}
|
|
count++
|
|
}
|
|
}
|
|
if count != len(deps) {
|
|
return "", 0, fmt.Errorf("Not enough deps written. Maybe the deps dictionary needs a bazelNameOverride or an old dep needs to be removed?")
|
|
}
|
|
|
|
if newWorkspaceFile, err := writeCommentsToWorkspace(workspaceFile, nativeRepos, providedRepos); err != nil {
|
|
fmt.Printf("Could not parse workspace file %s: %s\n", workspaceFile, err)
|
|
os.Exit(1)
|
|
} else {
|
|
// Atomically rename temp file to workspace. This should minimize the chance of corruption
|
|
// or writing a partial file if there is an error or the program is interrupted.
|
|
if err := os.Rename(newWorkspaceFile, workspaceFile); err != nil {
|
|
fmt.Printf("Could not write comments in workspace file %s -> %s: %s\n", newWorkspaceFile, workspaceFile, err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
return outputFile.Name(), count, nil
|
|
}
|
|
|
|
func writeCommentsToWorkspace(workspaceFile string, nativeRepos, providedRepos []string) (string, error) {
|
|
b, err := os.ReadFile(workspaceFile)
|
|
if err != nil {
|
|
return "", fmt.Errorf("Could not open %s: %s\n", workspaceFile, err)
|
|
}
|
|
newWorkspace, err := os.CreateTemp("", "workspace")
|
|
if err != nil {
|
|
return "", fmt.Errorf("Could not make tempfile: %s\n", err)
|
|
}
|
|
defer newWorkspace.Close()
|
|
|
|
workspaceContents := strings.Split(string(b), "\n")
|
|
|
|
sort.Strings(nativeRepos)
|
|
sort.Strings(providedRepos)
|
|
for _, line := range workspaceContents {
|
|
if _, err := newWorkspace.WriteString(line + "\n"); err != nil {
|
|
return "", err
|
|
}
|
|
if line == startListString {
|
|
break
|
|
}
|
|
}
|
|
for _, repoLine := range nativeRepos {
|
|
if _, err := newWorkspace.WriteString(repoLine + "\n"); err != nil {
|
|
return "", err
|
|
}
|
|
}
|
|
if _, err := newWorkspace.WriteString("#\n"); err != nil {
|
|
return "", err
|
|
}
|
|
for _, repoLine := range providedRepos {
|
|
if _, err := newWorkspace.WriteString(repoLine + "\n"); err != nil {
|
|
return "", err
|
|
}
|
|
}
|
|
if _, err := newWorkspace.WriteString(endListString + "\n"); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
pastEnd := false
|
|
// Skip the last line, which is blank. We don't want to end with two empty newlines.
|
|
for _, line := range workspaceContents[:len(workspaceContents)-1] {
|
|
if line == endListString {
|
|
pastEnd = true
|
|
continue
|
|
}
|
|
if !pastEnd {
|
|
continue
|
|
}
|
|
if _, err := newWorkspace.WriteString(line + "\n"); err != nil {
|
|
return "", err
|
|
}
|
|
}
|
|
|
|
return newWorkspace.Name(), nil
|
|
}
|
|
|
|
const (
|
|
startListString = `#### START GENERATED LIST OF THIRD_PARTY DEPS`
|
|
endListString = `#### END GENERATED LIST OF THIRD_PARTY DEPS`
|
|
)
|
|
|
|
const header = `"""
|
|
This file is auto-generated from //bazel/deps_parser
|
|
DO NOT MODIFY BY HAND.
|
|
Instead, do:
|
|
bazel run //bazel/deps_parser
|
|
"""
|
|
|
|
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository", "new_git_repository")
|
|
|
|
def git_repos_from_deps():
|
|
"""A list of native Bazel git rules to download third party git repositories
|
|
|
|
These are in the order they appear in //DEPS.
|
|
https://bazel.build/rules/lib/repo/git
|
|
"""`
|
|
|
|
func writeNewGitRepositoryRule(w io.StringWriter, bazelName, repo, rev string) error {
|
|
// TODO(kjlubick) In a newer version of Bazel, new_git_repository can be replaced with just
|
|
// git_repository
|
|
_, err := w.WriteString(fmt.Sprintf(`
|
|
new_git_repository(
|
|
name = "%s",
|
|
build_file = "@//bazel/external/%s:BUILD.bazel",
|
|
commit = "%s",
|
|
remote = "%s",
|
|
)
|
|
`, bazelName, bazelName, rev, repo))
|
|
return err
|
|
}
|
|
|
|
func writeGitRepositoryRule(w io.StringWriter, bazelName, repo, rev string) error {
|
|
_, err := w.WriteString(fmt.Sprintf(`
|
|
git_repository(
|
|
name = "%s",
|
|
commit = "%s",
|
|
remote = "%s",
|
|
)
|
|
`, bazelName, rev, repo))
|
|
return err
|
|
}
|