1// Copyright (c) 2024, Google Inc. 2// 3// Permission to use, copy, modify, and/or distribute this software for any 4// purpose with or without fee is hereby granted, provided that the above 5// copyright notice and this permission notice appear in all copies. 6// 7// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 10// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 12// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 13// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 15package main 16 17import ( 18 "bytes" 19 "cmp" 20 "encoding/json" 21 "fmt" 22 "path" 23 "path/filepath" 24 "slices" 25 "strings" 26 27 "boringssl.googlesource.com/boringssl/util/build" 28) 29 30// An InputTarget is a build target with build inputs that still need to be 31// pregenerated. All file lists in InputTarget are interpreted with glob 32// patterns as in filepath.Glob. 33type InputTarget struct { 34 build.Target 35 // ErrData contains a list of errordata files to combine into err_data.c. 36 ErrData []string `json:"err_data,omitempty"` 37 // The following fields define perlasm sources for the corresponding 38 // architecture. 39 PerlasmAarch64 []PerlasmSource `json:"perlasm_aarch64,omitempty"` 40 PerlasmArm []PerlasmSource `json:"perlasm_arm,omitempty"` 41 PerlasmX86 []PerlasmSource `json:"perlasm_x86,omitempty"` 42 PerlasmX86_64 []PerlasmSource `json:"perlasm_x86_64,omitempty"` 43} 44 45type PerlasmSource struct { 46 // Src the path to the input perlasm file. 47 Src string `json:"src"` 48 // Dst, if not empty, is base name of the destination file. If empty, this 49 // is determined from Src by default. It should be overriden if a single 50 // source file generates multiple functions (e.g. SHA-256 vs SHA-512) or 51 // multiple architectures (e.g. the "armx" files). 52 Dst string `json:"dst,omitempty"` 53 // Args is a list of extra parameters to pass to the script. 54 Args []string `json:"args,omitempty"` 55} 56 57// Pregenerate converts an input target to an output target. It returns the 58// result alongside a list of tasks that must be run to build the referenced 59// files. 60func (in *InputTarget) Pregenerate(name string) (out build.Target, tasks []Task, err error) { 61 // Expand wildcards. 62 out.Srcs, err = glob(in.Srcs) 63 if err != nil { 64 return 65 } 66 out.Hdrs, err = glob(in.Hdrs) 67 if err != nil { 68 return 69 } 70 out.InternalHdrs, err = glob(in.InternalHdrs) 71 if err != nil { 72 return 73 } 74 out.Asm, err = glob(in.Asm) 75 if err != nil { 76 return 77 } 78 out.Nasm, err = glob(in.Nasm) 79 if err != nil { 80 return 81 } 82 out.Data, err = glob(in.Data) 83 if err != nil { 84 return 85 } 86 87 addTask := func(list *[]string, t Task) { 88 tasks = append(tasks, t) 89 *list = append(*list, t.Destination()) 90 } 91 92 if len(in.ErrData) != 0 { 93 var inputs []string 94 inputs, err = glob(in.ErrData) 95 if err != nil { 96 return 97 } 98 addTask(&out.Srcs, &ErrDataTask{TargetName: name, Inputs: inputs}) 99 } 100 101 addPerlasmTask := func(list *[]string, p *PerlasmSource, fileSuffix string, args []string) { 102 dst := p.Dst 103 if len(p.Dst) == 0 { 104 dst = strings.TrimSuffix(path.Base(p.Src), ".pl") 105 } 106 dst = path.Join("gen", name, dst+fileSuffix) 107 args = append(slices.Clone(args), p.Args...) 108 addTask(list, &PerlasmTask{Src: p.Src, Dst: dst, Args: args}) 109 } 110 111 for _, p := range in.PerlasmAarch64 { 112 addPerlasmTask(&out.Asm, &p, "-apple.S", []string{"ios64"}) 113 addPerlasmTask(&out.Asm, &p, "-linux.S", []string{"linux64"}) 114 addPerlasmTask(&out.Asm, &p, "-win.S", []string{"win64"}) 115 } 116 for _, p := range in.PerlasmArm { 117 addPerlasmTask(&out.Asm, &p, "-linux.S", []string{"linux32"}) 118 } 119 for _, p := range in.PerlasmX86 { 120 addPerlasmTask(&out.Asm, &p, "-apple.S", []string{"macosx", "-fPIC"}) 121 addPerlasmTask(&out.Asm, &p, "-linux.S", []string{"elf", "-fPIC"}) 122 addPerlasmTask(&out.Nasm, &p, "-win.asm", []string{"win32n", "-fPIC"}) 123 } 124 for _, p := range in.PerlasmX86_64 { 125 addPerlasmTask(&out.Asm, &p, "-apple.S", []string{"macosx"}) 126 addPerlasmTask(&out.Asm, &p, "-linux.S", []string{"elf"}) 127 addPerlasmTask(&out.Nasm, &p, "-win.asm", []string{"nasm"}) 128 } 129 130 // Re-sort the modified fields. 131 slices.Sort(out.Srcs) 132 slices.Sort(out.Asm) 133 slices.Sort(out.Nasm) 134 135 return 136} 137 138func glob(paths []string) ([]string, error) { 139 var ret []string 140 for _, path := range paths { 141 if !strings.ContainsRune(path, '*') { 142 ret = append(ret, path) 143 continue 144 } 145 matches, err := filepath.Glob(path) 146 if err != nil { 147 return nil, err 148 } 149 if len(matches) == 0 { 150 return nil, fmt.Errorf("glob matched no files: %q", path) 151 } 152 // Switch from Windows to POSIX paths. 153 for _, match := range matches { 154 ret = append(ret, strings.ReplaceAll(match, "\\", "/")) 155 } 156 } 157 slices.Sort(ret) 158 return ret, nil 159} 160 161func sortedKeys[K cmp.Ordered, V any](m map[K]V) []K { 162 keys := make([]K, 0, len(m)) 163 for k := range m { 164 keys = append(keys, k) 165 } 166 slices.Sort(keys) 167 return keys 168} 169 170func writeHeader(b *bytes.Buffer, comment string) { 171 fmt.Fprintf(b, "%s Copyright (c) 2024, Google Inc.\n", comment) 172 fmt.Fprintf(b, "%s\n", comment) 173 fmt.Fprintf(b, "%s Permission to use, copy, modify, and/or distribute this software for any\n", comment) 174 fmt.Fprintf(b, "%s purpose with or without fee is hereby granted, provided that the above\n", comment) 175 fmt.Fprintf(b, "%s copyright notice and this permission notice appear in all copies.\n", comment) 176 fmt.Fprintf(b, "%s\n", comment) 177 fmt.Fprintf(b, "%s THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n", comment) 178 fmt.Fprintf(b, "%s WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n", comment) 179 fmt.Fprintf(b, "%s MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n", comment) 180 fmt.Fprintf(b, "%s SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n", comment) 181 fmt.Fprintf(b, "%s WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\n", comment) 182 fmt.Fprintf(b, "%s OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\n", comment) 183 fmt.Fprintf(b, "%s CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n", comment) 184 fmt.Fprintf(b, "%s\n", comment) 185 fmt.Fprintf(b, "%s Generated by go ./util/pregenerate. Do not edit manually.\n", comment) 186} 187 188func buildVariablesTask(targets map[string]build.Target, dst, comment string, writeVariable func(b *bytes.Buffer, name string, val []string)) Task { 189 return NewSimpleTask(dst, func() ([]byte, error) { 190 var b bytes.Buffer 191 writeHeader(&b, comment) 192 193 for _, name := range sortedKeys(targets) { 194 target := targets[name] 195 if len(target.Srcs) != 0 { 196 writeVariable(&b, name+"_sources", target.Srcs) 197 } 198 if len(target.Hdrs) != 0 { 199 writeVariable(&b, name+"_headers", target.Hdrs) 200 } 201 if len(target.InternalHdrs) != 0 { 202 writeVariable(&b, name+"_internal_headers", target.InternalHdrs) 203 } 204 if len(target.Asm) != 0 { 205 writeVariable(&b, name+"_sources_asm", target.Asm) 206 } 207 if len(target.Nasm) != 0 { 208 writeVariable(&b, name+"_sources_nasm", target.Nasm) 209 } 210 if len(target.Data) != 0 { 211 writeVariable(&b, name+"_data", target.Data) 212 } 213 } 214 215 return b.Bytes(), nil 216 }) 217} 218 219func writeBazelVariable(b *bytes.Buffer, name string, val []string) { 220 fmt.Fprintf(b, "\n%s = [\n", name) 221 for _, v := range val { 222 fmt.Fprintf(b, " %q,\n", v) 223 } 224 fmt.Fprintf(b, "]\n") 225} 226 227func writeCMakeVariable(b *bytes.Buffer, name string, val []string) { 228 fmt.Fprintf(b, "\nset(\n") 229 fmt.Fprintf(b, " %s\n\n", strings.ToUpper(name)) 230 for _, v := range val { 231 fmt.Fprintf(b, " %s\n", v) 232 } 233 fmt.Fprintf(b, ")\n") 234} 235 236func writeMakeVariable(b *bytes.Buffer, name string, val []string) { 237 fmt.Fprintf(b, "\n%s := \\\n", name) 238 for i, v := range val { 239 if i == len(val)-1 { 240 fmt.Fprintf(b, " %s\n", v) 241 } else { 242 fmt.Fprintf(b, " %s \\\n", v) 243 } 244 } 245} 246 247func writeGNVariable(b *bytes.Buffer, name string, val []string) { 248 fmt.Fprintf(b, "\n%s = [\n", name) 249 for _, v := range val { 250 fmt.Fprintf(b, " %q,\n", v) 251 } 252 fmt.Fprintf(b, "]\n") 253} 254 255func jsonTask(targets map[string]build.Target, dst string) Task { 256 return NewSimpleTask(dst, func() ([]byte, error) { 257 return json.MarshalIndent(targets, "", " ") 258 }) 259} 260 261func soongTask(targets map[string]build.Target, dst string) Task { 262 return NewSimpleTask(dst, func() ([]byte, error) { 263 var b bytes.Buffer 264 writeHeader(&b, "//") 265 266 writeAttribute := func(indent, name string, val []string) { 267 fmt.Fprintf(&b, "%s%s: [\n", indent, name) 268 for _, v := range val { 269 fmt.Fprintf(&b, "%s %q,\n", indent, v) 270 } 271 fmt.Fprintf(&b, "%s],\n", indent) 272 273 } 274 275 for _, name := range sortedKeys(targets) { 276 target := targets[name] 277 fmt.Fprintf(&b, "\ncc_defaults {\n") 278 fmt.Fprintf(&b, " name: %q\n", "boringssl_"+name+"_sources") 279 if len(target.Srcs) != 0 { 280 writeAttribute(" ", "srcs", target.Srcs) 281 } 282 if len(target.Data) != 0 { 283 writeAttribute(" ", "data", target.Data) 284 } 285 if len(target.Asm) != 0 { 286 fmt.Fprintf(&b, " target: {\n") 287 // Only emit asm for Linux. On Windows, BoringSSL requires NASM, which is 288 // not available in AOSP. On Darwin, the assembly works fine, but it 289 // conflicts with Android's FIPS build. See b/294399371. 290 fmt.Fprintf(&b, " linux: {\n") 291 writeAttribute(" ", "srcs", target.Asm) 292 fmt.Fprintf(&b, " },\n") 293 fmt.Fprintf(&b, " darwin: {\n") 294 fmt.Fprintf(&b, " cflags: [\"-DOPENSSL_NO_ASM\"],\n") 295 fmt.Fprintf(&b, " },\n") 296 fmt.Fprintf(&b, " windows: {\n") 297 fmt.Fprintf(&b, " cflags: [\"-DOPENSSL_NO_ASM\"],\n") 298 fmt.Fprintf(&b, " },\n") 299 fmt.Fprintf(&b, " },\n") 300 } 301 fmt.Fprintf(&b, "},\n") 302 } 303 304 return b.Bytes(), nil 305 }) 306} 307 308func MakeBuildFiles(targets map[string]build.Target) []Task { 309 // TODO(crbug.com/boringssl/542): Generate the build files for the other 310 // types as well. 311 return []Task{ 312 buildVariablesTask(targets, "gen/sources.bzl", "#", writeBazelVariable), 313 buildVariablesTask(targets, "gen/sources.cmake", "#", writeCMakeVariable), 314 jsonTask(targets, "gen/sources.json"), 315 } 316} 317