xref: /aosp_15_r20/external/bazelbuild-rules_cc/tools/migration/crosstool_to_starlark_lib.go (revision eed53cd41c5909d05eedc7ad9720bb158fd93452)
1/*
2Package crosstooltostarlarklib provides the Transform method
3for conversion of a CROSSTOOL file to a Starlark rule.
4
5https://github.com/bazelbuild/bazel/issues/5380
6*/
7package crosstooltostarlarklib
8
9import (
10	"bytes"
11	"errors"
12	"fmt"
13	"sort"
14	"strings"
15
16	crosstoolpb "third_party/com/github/bazelbuild/bazel/src/main/protobuf/crosstool_config_go_proto"
17)
18
19// CToolchainIdentifier is what we'll use to differ between CToolchains
20// If a CToolchain can be distinguished from the other CToolchains
21// by only one of the fields (eg if cpu is different for each CToolchain
22// then only that field will be set.
23type CToolchainIdentifier struct {
24	cpu      string
25	compiler string
26}
27
28// Writes the load statement for the cc_toolchain_config_lib
29func getCcToolchainConfigHeader() string {
30	return `load("@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl",
31    "action_config",
32    "artifact_name_pattern",
33    "env_entry",
34    "env_set",
35    "feature",
36    "feature_set",
37    "flag_group",
38    "flag_set",
39    "make_variable",
40    "tool",
41    "tool_path",
42    "variable_with_value",
43    "with_feature_set",
44)
45`
46}
47
48var allCompileActions = []string{
49	"c-compile",
50	"c++-compile",
51	"linkstamp-compile",
52	"assemble",
53	"preprocess-assemble",
54	"c++-header-parsing",
55	"c++-module-compile",
56	"c++-module-codegen",
57	"clif-match",
58	"lto-backend",
59}
60
61var allCppCompileActions = []string{
62	"c++-compile",
63	"linkstamp-compile",
64	"c++-header-parsing",
65	"c++-module-compile",
66	"c++-module-codegen",
67	"clif-match",
68}
69
70var preprocessorCompileActions = []string{
71	"c-compile",
72	"c++-compile",
73	"linkstamp-compile",
74	"preprocess-assemble",
75	"c++-header-parsing",
76	"c++-module-compile",
77	"clif-match",
78}
79
80var codegenCompileActions = []string{
81	"c-compile",
82	"c++-compile",
83	"linkstamp-compile",
84	"assemble",
85	"preprocess-assemble",
86	"c++-module-codegen",
87	"lto-backend",
88}
89
90var allLinkActions = []string{
91	"c++-link-executable",
92	"c++-link-dynamic-library",
93	"c++-link-nodeps-dynamic-library",
94}
95
96var actionNames = map[string]string{
97	"c-compile":                       "ACTION_NAMES.c_compile",
98	"c++-compile":                     "ACTION_NAMES.cpp_compile",
99	"linkstamp-compile":               "ACTION_NAMES.linkstamp_compile",
100	"cc-flags-make-variable":          "ACTION_NAMES.cc_flags_make_variable",
101	"c++-module-codegen":              "ACTION_NAMES.cpp_module_codegen",
102	"c++-header-parsing":              "ACTION_NAMES.cpp_header_parsing",
103	"c++-module-compile":              "ACTION_NAMES.cpp_module_compile",
104	"assemble":                        "ACTION_NAMES.assemble",
105	"preprocess-assemble":             "ACTION_NAMES.preprocess_assemble",
106	"lto-indexing":                    "ACTION_NAMES.lto_indexing",
107	"lto-backend":                     "ACTION_NAMES.lto_backend",
108	"c++-link-executable":             "ACTION_NAMES.cpp_link_executable",
109	"c++-link-dynamic-library":        "ACTION_NAMES.cpp_link_dynamic_library",
110	"c++-link-nodeps-dynamic-library": "ACTION_NAMES.cpp_link_nodeps_dynamic_library",
111	"c++-link-static-library":         "ACTION_NAMES.cpp_link_static_library",
112	"strip":                           "ACTION_NAMES.strip",
113	"objc-compile":                    "ACTION_NAMES.objc_compile",
114	"objc++-compile":                  "ACTION_NAMES.objcpp_compile",
115	"clif-match":                      "ACTION_NAMES.clif_match",
116// 	"objcopy_embed_data":              "ACTION_NAMES.objcopy_embed_data", // copybara-comment-this-out-please
117// 	"ld_embed_data":                   "ACTION_NAMES.ld_embed_data",      // copybara-comment-this-out-please
118}
119
120func getLoadActionsStmt() string {
121	return "load(\"@bazel_tools//tools/build_defs/cc:action_names.bzl\", \"ACTION_NAMES\")\n\n"
122}
123
124// Returns a map {toolchain_identifier : CToolchainIdentifier}
125func toolchainToCToolchainIdentifier(
126	crosstool *crosstoolpb.CrosstoolRelease) map[string]CToolchainIdentifier {
127	cpuToCompiler := make(map[string][]string)
128	compilerToCPU := make(map[string][]string)
129	var cpus []string
130	var compilers []string
131	var identifiers []string
132	res := make(map[string]CToolchainIdentifier)
133	for _, cToolchain := range crosstool.GetToolchain() {
134		cpu := cToolchain.GetTargetCpu()
135		compiler := cToolchain.GetCompiler()
136
137		cpuToCompiler[cpu] = append(cpuToCompiler[cpu], compiler)
138		compilerToCPU[compiler] = append(compilerToCPU[compiler], cpu)
139
140		cpus = append(cpus, cToolchain.GetTargetCpu())
141		compilers = append(compilers, cToolchain.GetCompiler())
142		identifiers = append(identifiers, cToolchain.GetToolchainIdentifier())
143	}
144
145	for i := range cpus {
146		if len(cpuToCompiler[cpus[i]]) == 1 {
147			// if cpu is unique among CToolchains, we don't need the compiler field
148			res[identifiers[i]] = CToolchainIdentifier{cpu: cpus[i], compiler: ""}
149		} else {
150			res[identifiers[i]] = CToolchainIdentifier{
151				cpu:      cpus[i],
152				compiler: compilers[i],
153			}
154		}
155	}
156	return res
157}
158
159func getConditionStatementForCToolchainIdentifier(identifier CToolchainIdentifier) string {
160	if identifier.compiler != "" {
161		return fmt.Sprintf(
162			"ctx.attr.cpu == \"%s\" and ctx.attr.compiler == \"%s\"",
163			identifier.cpu,
164			identifier.compiler)
165	}
166	return fmt.Sprintf("ctx.attr.cpu == \"%s\"", identifier.cpu)
167}
168
169func isArrayPrefix(prefix []string, arr []string) bool {
170	if len(prefix) > len(arr) {
171		return false
172	}
173	for i := 0; i < len(prefix); i++ {
174		if arr[i] != prefix[i] {
175			return false
176		}
177	}
178	return true
179}
180
181func isAllCompileActions(actions []string) (bool, []string) {
182	if isArrayPrefix(allCompileActions, actions) {
183		return true, actions[len(allCompileActions):]
184	}
185	return false, actions
186}
187
188func isAllCppCompileActions(actions []string) (bool, []string) {
189	if isArrayPrefix(allCppCompileActions, actions) {
190		return true, actions[len(allCppCompileActions):]
191	}
192	return false, actions
193}
194
195func isPreprocessorCompileActions(actions []string) (bool, []string) {
196	if isArrayPrefix(preprocessorCompileActions, actions) {
197		return true, actions[len(preprocessorCompileActions):]
198	}
199	return false, actions
200}
201
202func isCodegenCompileActions(actions []string) (bool, []string) {
203	if isArrayPrefix(codegenCompileActions, actions) {
204		return true, actions[len(codegenCompileActions):]
205	}
206	return false, actions
207}
208
209func isAllLinkActions(actions []string) (bool, []string) {
210	if isArrayPrefix(allLinkActions, actions) {
211		return true, actions[len(allLinkActions):]
212	}
213	return false, actions
214}
215
216func getActionNames(actions []string) []string {
217	var res []string
218	for _, el := range actions {
219		if name, ok := actionNames[el]; ok {
220			res = append(res, name)
221		} else {
222			res = append(res, "\""+el+"\"")
223		}
224	}
225	return res
226}
227
228func getListOfActions(name string, depth int) string {
229	var res []string
230	if name == "all_compile_actions" {
231		res = getActionNames(allCompileActions)
232	} else if name == "all_cpp_compile_actions" {
233		res = getActionNames(allCppCompileActions)
234	} else if name == "preprocessor_compile_actions" {
235		res = getActionNames(preprocessorCompileActions)
236	} else if name == "codegen_compile_actions" {
237		res = getActionNames(codegenCompileActions)
238	} else if name == "all_link_actions" {
239		res = getActionNames(allLinkActions)
240	}
241	stmt := fmt.Sprintf("%s%s = %s\n\n", getTabs(depth),
242		name, makeStringArr(res, depth /* isPlainString= */, false))
243	return stmt
244}
245
246func processActions(actions []string, depth int) []string {
247	var res []string
248	var ok bool
249	initLen := len(actions)
250	if ok, actions = isAllCompileActions(actions); ok {
251		res = append(res, "all_compile_actions")
252	}
253	if ok, actions = isAllCppCompileActions(actions); ok {
254		res = append(res, "all_cpp_compile_actions")
255	}
256	if ok, actions = isPreprocessorCompileActions(actions); ok {
257		res = append(res, "preprocessor_compile_actions")
258	}
259	if ok, actions = isCodegenCompileActions(actions); ok {
260		res = append(res, "codegen_actions")
261	}
262	if ok, actions = isAllLinkActions(actions); ok {
263		res = append(res, "all_link_actions")
264	}
265	if len(actions) != 0 {
266		actions = getActionNames(actions)
267		newDepth := depth + 1
268		if len(actions) != initLen {
269			newDepth++
270		}
271		res = append(res, makeStringArr(actions, newDepth /* isPlainString= */, false))
272	}
273	return res
274}
275
276func getUniqueValues(arr []string) []string {
277	valuesSet := make(map[string]bool)
278	for _, val := range arr {
279		valuesSet[val] = true
280	}
281	var uniques []string
282	for val, _ := range valuesSet {
283		uniques = append(uniques, val)
284	}
285	sort.Strings(uniques)
286	return uniques
287}
288
289func getRule(cToolchainIdentifiers map[string]CToolchainIdentifier,
290	allowedCompilers []string) string {
291	cpus := make(map[string]bool)
292	shouldUseCompilerAttribute := false
293	for _, val := range cToolchainIdentifiers {
294		cpus[val.cpu] = true
295		if val.compiler != "" {
296			shouldUseCompilerAttribute = true
297		}
298	}
299
300	var cpuValues []string
301	for cpu := range cpus {
302		cpuValues = append(cpuValues, cpu)
303	}
304
305	var args []string
306	sort.Strings(cpuValues)
307	args = append(args,
308		fmt.Sprintf(
309			`"cpu": attr.string(mandatory=True, values=["%s"]),`,
310			strings.Join(cpuValues, "\", \"")))
311	if shouldUseCompilerAttribute {
312		// If there are two CToolchains that share the cpu we need the compiler attribute
313		// for our cc_toolchain_config rule.
314		allowedCompilers = getUniqueValues(allowedCompilers)
315		args = append(args,
316			fmt.Sprintf(`"compiler": attr.string(mandatory=True, values=["%s"]),`,
317				strings.Join(allowedCompilers, "\", \"")))
318	}
319	return fmt.Sprintf(`cc_toolchain_config =  rule(
320    implementation = _impl,
321    attrs = {
322        %s
323    },
324    provides = [CcToolchainConfigInfo],
325    executable = True,
326)
327`, strings.Join(args, "\n        "))
328}
329
330func getImplHeader() string {
331	return "def _impl(ctx):\n"
332}
333
334func getStringStatement(crosstool *crosstoolpb.CrosstoolRelease,
335	cToolchainIdentifiers map[string]CToolchainIdentifier, field string,
336	depth int) string {
337
338	identifiers := getToolchainIdentifiers(crosstool)
339	var fieldValues []string
340	if field == "toolchain_identifier" {
341		fieldValues = getToolchainIdentifiers(crosstool)
342	} else if field == "host_system_name" {
343		fieldValues = getHostSystemNames(crosstool)
344	} else if field == "target_system_name" {
345		fieldValues = getTargetSystemNames(crosstool)
346	} else if field == "target_cpu" {
347		fieldValues = getTargetCpus(crosstool)
348	} else if field == "target_libc" {
349		fieldValues = getTargetLibcs(crosstool)
350	} else if field == "compiler" {
351		fieldValues = getCompilers(crosstool)
352	} else if field == "abi_version" {
353		fieldValues = getAbiVersions(crosstool)
354	} else if field == "abi_libc_version" {
355		fieldValues = getAbiLibcVersions(crosstool)
356	} else if field == "cc_target_os" {
357		fieldValues = getCcTargetOss(crosstool)
358	} else if field == "builtin_sysroot" {
359		fieldValues = getBuiltinSysroots(crosstool)
360	}
361
362	mappedValuesToIds := getMappedStringValuesToIdentifiers(identifiers, fieldValues)
363	return getAssignmentStatement(field, mappedValuesToIds, crosstool,
364		cToolchainIdentifiers, depth /* isPlainString= */, true /* shouldFail= */, true)
365}
366
367func getFeatures(crosstool *crosstoolpb.CrosstoolRelease) (
368	map[string][]string, map[string]map[string][]string, error) {
369	featureNameToFeature := make(map[string]map[string][]string)
370	toolchainToFeatures := make(map[string][]string)
371	for _, toolchain := range crosstool.GetToolchain() {
372		id := toolchain.GetToolchainIdentifier()
373		if len(toolchain.GetFeature()) == 0 {
374			toolchainToFeatures[id] = []string{}
375		}
376		for _, feature := range toolchain.GetFeature() {
377			featureName := strings.ToLower(feature.GetName()) + "_feature"
378			featureName = strings.Replace(featureName, "+", "p", -1)
379			featureName = strings.Replace(featureName, ".", "_", -1)
380			featureName = strings.Replace(featureName, "-", "_", -1)
381			stringFeature, err := parseFeature(feature, 1)
382			if err != nil {
383				return nil, nil, fmt.Errorf(
384					"Error in feature '%s': %v", feature.GetName(), err)
385			}
386			if _, ok := featureNameToFeature[featureName]; !ok {
387				featureNameToFeature[featureName] = make(map[string][]string)
388			}
389			featureNameToFeature[featureName][stringFeature] = append(
390				featureNameToFeature[featureName][stringFeature], id)
391			toolchainToFeatures[id] = append(toolchainToFeatures[id], featureName)
392		}
393	}
394	return toolchainToFeatures, featureNameToFeature, nil
395}
396
397func getFeaturesDeclaration(crosstool *crosstoolpb.CrosstoolRelease,
398	cToolchainIdentifiers map[string]CToolchainIdentifier,
399	featureNameToFeature map[string]map[string][]string, depth int) string {
400	var res []string
401	for featureName, featureStringToID := range featureNameToFeature {
402		res = append(res,
403			getAssignmentStatement(
404				featureName,
405				featureStringToID,
406				crosstool,
407				cToolchainIdentifiers,
408				depth,
409				/* isPlainString= */ false,
410				/* shouldFail= */ false))
411	}
412	return strings.Join(res, "")
413}
414
415func getFeaturesStmt(cToolchainIdentifiers map[string]CToolchainIdentifier,
416	toolchainToFeatures map[string][]string, depth int) string {
417	var res []string
418	arrToIdentifier := make(map[string][]string)
419	for id, features := range toolchainToFeatures {
420		arrayString := strings.Join(features, "{arrayFieldDelimiter}")
421		arrToIdentifier[arrayString] = append(arrToIdentifier[arrayString], id)
422	}
423	res = append(res,
424		getStringArrStatement(
425			"features",
426			arrToIdentifier,
427			cToolchainIdentifiers,
428			depth,
429			/* isPlainString= */ false))
430	return strings.Join(res, "\n")
431}
432
433func getActions(crosstool *crosstoolpb.CrosstoolRelease) (
434	map[string][]string, map[string]map[string][]string, error) {
435	actionNameToAction := make(map[string]map[string][]string)
436	toolchainToActions := make(map[string][]string)
437	for _, toolchain := range crosstool.GetToolchain() {
438		id := toolchain.GetToolchainIdentifier()
439		var actionName string
440		if len(toolchain.GetActionConfig()) == 0 {
441			toolchainToActions[id] = []string{}
442		}
443		for _, action := range toolchain.GetActionConfig() {
444			if aName, ok := actionNames[action.GetActionName()]; ok {
445				actionName = aName
446			} else {
447				actionName = strings.ToLower(action.GetActionName())
448				actionName = strings.Replace(actionName, "+", "p", -1)
449				actionName = strings.Replace(actionName, ".", "_", -1)
450				actionName = strings.Replace(actionName, "-", "_", -1)
451			}
452			stringAction, err := parseAction(action, 1)
453			if err != nil {
454				return nil, nil, fmt.Errorf(
455					"Error in action_config '%s': %v", action.GetActionName(), err)
456			}
457			if _, ok := actionNameToAction[actionName]; !ok {
458				actionNameToAction[actionName] = make(map[string][]string)
459			}
460			actionNameToAction[actionName][stringAction] = append(
461				actionNameToAction[actionName][stringAction], id)
462			toolchainToActions[id] = append(
463				toolchainToActions[id],
464				strings.TrimPrefix(strings.ToLower(actionName), "action_names.")+"_action")
465		}
466	}
467	return toolchainToActions, actionNameToAction, nil
468}
469
470func getActionConfigsDeclaration(
471	crosstool *crosstoolpb.CrosstoolRelease,
472	cToolchainIdentifiers map[string]CToolchainIdentifier,
473	actionNameToAction map[string]map[string][]string, depth int) string {
474	var res []string
475	for actionName, actionStringToID := range actionNameToAction {
476		variableName := strings.TrimPrefix(strings.ToLower(actionName), "action_names.") + "_action"
477		res = append(res,
478			getAssignmentStatement(
479				variableName,
480				actionStringToID,
481				crosstool,
482				cToolchainIdentifiers,
483				depth,
484				/* isPlainString= */ false,
485				/* shouldFail= */ false))
486	}
487	return strings.Join(res, "")
488}
489
490func getActionConfigsStmt(
491	cToolchainIdentifiers map[string]CToolchainIdentifier,
492	toolchainToActions map[string][]string, depth int) string {
493	var res []string
494	arrToIdentifier := make(map[string][]string)
495	for id, actions := range toolchainToActions {
496		var arrayString string
497		arrayString = strings.Join(actions, "{arrayFieldDelimiter}")
498		arrToIdentifier[arrayString] = append(arrToIdentifier[arrayString], id)
499	}
500	res = append(res,
501		getStringArrStatement(
502			"action_configs",
503			arrToIdentifier,
504			cToolchainIdentifiers,
505			depth,
506			/* isPlainString= */ false))
507	return strings.Join(res, "\n")
508}
509
510func parseAction(action *crosstoolpb.CToolchain_ActionConfig, depth int) (string, error) {
511	actionName := action.GetActionName()
512	aName := ""
513	if val, ok := actionNames[actionName]; ok {
514		aName = val
515	} else {
516		aName = "\"" + action.GetActionName() + "\""
517	}
518	name := fmt.Sprintf("action_name = %s", aName)
519	fields := []string{name}
520	if action.GetEnabled() {
521		fields = append(fields, "enabled = True")
522	}
523	if len(action.GetFlagSet()) != 0 {
524		flagSets, err := parseFlagSets(action.GetFlagSet(), depth+1)
525		if err != nil {
526			return "", err
527		}
528		fields = append(fields, "flag_sets = "+flagSets)
529	}
530	if len(action.GetImplies()) != 0 {
531		implies := "implies = " +
532			makeStringArr(action.GetImplies(), depth+1 /* isPlainString= */, true)
533		fields = append(fields, implies)
534	}
535	if len(action.GetTool()) != 0 {
536		tools := "tools = " + parseTools(action.GetTool(), depth+1)
537		fields = append(fields, tools)
538	}
539	return createObject("action_config", fields, depth), nil
540}
541
542func getStringArrStatement(attr string, arrValToIds map[string][]string,
543	cToolchainIdentifiers map[string]CToolchainIdentifier, depth int, plainString bool) string {
544	var b bytes.Buffer
545	if len(arrValToIds) == 0 {
546		b.WriteString(fmt.Sprintf("%s%s = []\n", getTabs(depth), attr))
547	} else if len(arrValToIds) == 1 {
548		for value := range arrValToIds {
549			var arr []string
550			if value == "" {
551				arr = []string{}
552			} else if value == "None" {
553				b.WriteString(fmt.Sprintf("%s%s = None\n", getTabs(depth), attr))
554				break
555			} else {
556				arr = strings.Split(value, "{arrayFieldDelimiter}")
557			}
558			b.WriteString(
559				fmt.Sprintf(
560					"%s%s = %s\n",
561					getTabs(depth),
562					attr,
563					makeStringArr(arr, depth+1, plainString)))
564			break
565		}
566	} else {
567		first := true
568		var keys []string
569		for k := range arrValToIds {
570			keys = append(keys, k)
571		}
572		sort.Strings(keys)
573		for _, value := range keys {
574			ids := arrValToIds[value]
575			branch := "elif"
576			if first {
577				branch = "if"
578			}
579			first = false
580			var arr []string
581			if value == "" {
582				arr = []string{}
583			} else if value == "None" {
584				b.WriteString(
585					getIfStatement(
586						branch, ids, attr, "None", cToolchainIdentifiers,
587						depth /* isPlainString= */, true))
588				continue
589			} else {
590				arr = strings.Split(value, "{arrayFieldDelimiter}")
591			}
592			b.WriteString(
593				getIfStatement(branch, ids, attr,
594					makeStringArr(arr, depth+1, plainString),
595					cToolchainIdentifiers, depth /* isPlainString= */, false))
596		}
597		b.WriteString(fmt.Sprintf("%selse:\n%sfail(\"Unreachable\")\n", getTabs(depth), getTabs(depth+1)))
598	}
599	b.WriteString("\n")
600	return b.String()
601}
602
603func getStringArr(crosstool *crosstoolpb.CrosstoolRelease,
604	cToolchainIdentifiers map[string]CToolchainIdentifier, attr string, depth int) string {
605	var res []string
606	arrToIdentifier := make(map[string][]string)
607	for _, toolchain := range crosstool.GetToolchain() {
608		id := toolchain.GetToolchainIdentifier()
609		arrayString := strings.Join(getArrField(attr, toolchain), "{arrayFieldDelimiter}")
610		arrToIdentifier[arrayString] = append(arrToIdentifier[arrayString], id)
611	}
612	statement := getStringArrStatement(attr, arrToIdentifier, cToolchainIdentifiers, depth /* isPlainString= */, true)
613	res = append(res, statement)
614	return strings.Join(res, "\n")
615}
616
617func getArrField(attr string, toolchain *crosstoolpb.CToolchain) []string {
618	var arr []string
619	if attr == "cxx_builtin_include_directories" {
620		arr = toolchain.GetCxxBuiltinIncludeDirectory()
621	}
622	return arr
623}
624
625func getTabs(depth int) string {
626	var res string
627	for i := 0; i < depth; i++ {
628		res = res + "    "
629	}
630	return res
631}
632
633func createObject(objtype string, fields []string, depth int) string {
634	if len(fields) == 0 {
635		return objtype + "()"
636	}
637	singleLine := objtype + "(" + strings.Join(fields, ", ") + ")"
638	if len(singleLine) < 60 {
639		return singleLine
640	}
641	return objtype +
642		"(\n" +
643		getTabs(depth+1) +
644		strings.Join(fields, ",\n"+getTabs(depth+1)) +
645		",\n" + getTabs(depth) +
646		")"
647}
648
649func getArtifactNamePatterns(crosstool *crosstoolpb.CrosstoolRelease,
650	cToolchainIdentifiers map[string]CToolchainIdentifier, depth int) string {
651	var res []string
652	artifactToIds := make(map[string][]string)
653	for _, toolchain := range crosstool.GetToolchain() {
654		artifactNamePatterns := parseArtifactNamePatterns(
655			toolchain.GetArtifactNamePattern(),
656			depth)
657		artifactToIds[artifactNamePatterns] = append(
658			artifactToIds[artifactNamePatterns],
659			toolchain.GetToolchainIdentifier())
660	}
661	res = append(res,
662		getAssignmentStatement(
663			"artifact_name_patterns",
664			artifactToIds,
665			crosstool,
666			cToolchainIdentifiers,
667			depth,
668			/* isPlainString= */ false,
669			/* shouldFail= */ true))
670	return strings.Join(res, "\n")
671}
672
673func parseArtifactNamePatterns(
674	artifactNamePatterns []*crosstoolpb.CToolchain_ArtifactNamePattern, depth int) string {
675	var res []string
676	for _, pattern := range artifactNamePatterns {
677		res = append(res, parseArtifactNamePattern(pattern, depth+1))
678	}
679	return makeStringArr(res, depth /* isPlainString= */, false)
680}
681
682func parseArtifactNamePattern(
683	artifactNamePattern *crosstoolpb.CToolchain_ArtifactNamePattern, depth int) string {
684	categoryName := fmt.Sprintf("category_name = \"%s\"", artifactNamePattern.GetCategoryName())
685	prefix := fmt.Sprintf("prefix = \"%s\"", artifactNamePattern.GetPrefix())
686	extension := fmt.Sprintf("extension = \"%s\"", artifactNamePattern.GetExtension())
687	fields := []string{categoryName, prefix, extension}
688	return createObject("artifact_name_pattern", fields, depth)
689}
690
691func parseFeature(feature *crosstoolpb.CToolchain_Feature, depth int) (string, error) {
692	name := fmt.Sprintf("name = \"%s\"", feature.GetName())
693
694	fields := []string{name}
695	if feature.GetEnabled() {
696		fields = append(fields, "enabled = True")
697	}
698
699	if len(feature.GetFlagSet()) > 0 {
700		flagSets, err := parseFlagSets(feature.GetFlagSet(), depth+1)
701		if err != nil {
702			return "", err
703		}
704		fields = append(fields, "flag_sets = "+flagSets)
705	}
706	if len(feature.GetEnvSet()) > 0 {
707		envSets := "env_sets = " + parseEnvSets(feature.GetEnvSet(), depth+1)
708		fields = append(fields, envSets)
709	}
710	if len(feature.GetRequires()) > 0 {
711		requires := "requires = " + parseFeatureSets(feature.GetRequires(), depth+1)
712		fields = append(fields, requires)
713	}
714	if len(feature.GetImplies()) > 0 {
715		implies := "implies = " +
716			makeStringArr(feature.GetImplies(), depth+1 /* isPlainString= */, true)
717		fields = append(fields, implies)
718	}
719	if len(feature.GetProvides()) > 0 {
720		provides := "provides = " +
721			makeStringArr(feature.GetProvides(), depth+1 /* isPlainString= */, true)
722		fields = append(fields, provides)
723	}
724	return createObject("feature", fields, depth), nil
725}
726
727func parseFlagSets(flagSets []*crosstoolpb.CToolchain_FlagSet, depth int) (string, error) {
728	var res []string
729	for _, flagSet := range flagSets {
730		parsedFlagset, err := parseFlagSet(flagSet, depth+1)
731		if err != nil {
732			return "", err
733		}
734		res = append(res, parsedFlagset)
735	}
736	return makeStringArr(res, depth /* isPlainString= */, false), nil
737}
738
739func parseFlagSet(flagSet *crosstoolpb.CToolchain_FlagSet, depth int) (string, error) {
740	var fields []string
741	if len(flagSet.GetAction()) > 0 {
742		actionArr := processActions(flagSet.GetAction(), depth)
743		actions := "actions = " + strings.Join(actionArr, " +\n"+getTabs(depth+2))
744		fields = append(fields, actions)
745	}
746	if len(flagSet.GetFlagGroup()) > 0 {
747		flagGroups, err := parseFlagGroups(flagSet.GetFlagGroup(), depth+1)
748		if err != nil {
749			return "", err
750		}
751		fields = append(fields, "flag_groups = "+flagGroups)
752	}
753	if len(flagSet.GetWithFeature()) > 0 {
754		withFeatures := "with_features = " +
755			parseWithFeatureSets(flagSet.GetWithFeature(), depth+1)
756		fields = append(fields, withFeatures)
757	}
758	return createObject("flag_set", fields, depth), nil
759}
760
761func parseFlagGroups(flagGroups []*crosstoolpb.CToolchain_FlagGroup, depth int) (string, error) {
762	var res []string
763	for _, flagGroup := range flagGroups {
764		flagGroupString, err := parseFlagGroup(flagGroup, depth+1)
765		if err != nil {
766			return "", err
767		}
768		res = append(res, flagGroupString)
769	}
770	return makeStringArr(res, depth /* isPlainString= */, false), nil
771}
772
773func parseFlagGroup(flagGroup *crosstoolpb.CToolchain_FlagGroup, depth int) (string, error) {
774	var res []string
775	if len(flagGroup.GetFlag()) != 0 {
776		res = append(res, "flags = "+makeStringArr(flagGroup.GetFlag(), depth+1, true))
777	}
778	if flagGroup.GetIterateOver() != "" {
779		res = append(res, fmt.Sprintf("iterate_over = \"%s\"", flagGroup.GetIterateOver()))
780	}
781	if len(flagGroup.GetFlagGroup()) != 0 {
782		flagGroupString, err := parseFlagGroups(flagGroup.GetFlagGroup(), depth+1)
783		if err != nil {
784			return "", err
785		}
786		res = append(res, "flag_groups = "+flagGroupString)
787	}
788	if len(flagGroup.GetExpandIfAllAvailable()) > 1 {
789		return "", errors.New("Flag group must not have more than one 'expand_if_all_available' field")
790	}
791	if len(flagGroup.GetExpandIfAllAvailable()) != 0 {
792		res = append(res,
793			fmt.Sprintf(
794				"expand_if_available = \"%s\"",
795				flagGroup.GetExpandIfAllAvailable()[0]))
796	}
797	if len(flagGroup.GetExpandIfNoneAvailable()) > 1 {
798		return "", errors.New("Flag group must not have more than one 'expand_if_none_available' field")
799	}
800	if len(flagGroup.GetExpandIfNoneAvailable()) != 0 {
801		res = append(res,
802			fmt.Sprintf(
803				"expand_if_not_available = \"%s\"",
804				flagGroup.GetExpandIfNoneAvailable()[0]))
805	}
806	if flagGroup.GetExpandIfTrue() != "" {
807		res = append(res, fmt.Sprintf("expand_if_true = \"%s\"",
808			flagGroup.GetExpandIfTrue()))
809	}
810	if flagGroup.GetExpandIfFalse() != "" {
811		res = append(res, fmt.Sprintf("expand_if_false = \"%s\"",
812			flagGroup.GetExpandIfFalse()))
813	}
814	if flagGroup.GetExpandIfEqual() != nil {
815		res = append(res,
816			"expand_if_equal = "+parseVariableWithValue(
817				flagGroup.GetExpandIfEqual(), depth+1))
818	}
819	return createObject("flag_group", res, depth), nil
820}
821
822func parseVariableWithValue(variable *crosstoolpb.CToolchain_VariableWithValue, depth int) string {
823	variableName := fmt.Sprintf("name = \"%s\"", variable.GetVariable())
824	value := fmt.Sprintf("value = \"%s\"", variable.GetValue())
825	return createObject("variable_with_value", []string{variableName, value}, depth)
826}
827
828func getToolPaths(crosstool *crosstoolpb.CrosstoolRelease,
829	cToolchainIdentifiers map[string]CToolchainIdentifier, depth int) string {
830	var res []string
831	toolPathsToIds := make(map[string][]string)
832	for _, toolchain := range crosstool.GetToolchain() {
833		toolPaths := parseToolPaths(toolchain.GetToolPath(), depth)
834		toolPathsToIds[toolPaths] = append(
835			toolPathsToIds[toolPaths],
836			toolchain.GetToolchainIdentifier())
837	}
838	res = append(res,
839		getAssignmentStatement(
840			"tool_paths",
841			toolPathsToIds,
842			crosstool,
843			cToolchainIdentifiers,
844			depth,
845			/* isPlainString= */ false,
846			/* shouldFail= */ true))
847	return strings.Join(res, "\n")
848}
849
850func parseToolPaths(toolPaths []*crosstoolpb.ToolPath, depth int) string {
851	var res []string
852	for _, toolPath := range toolPaths {
853		res = append(res, parseToolPath(toolPath, depth+1))
854	}
855	return makeStringArr(res, depth /* isPlainString= */, false)
856}
857
858func parseToolPath(toolPath *crosstoolpb.ToolPath, depth int) string {
859	name := fmt.Sprintf("name = \"%s\"", toolPath.GetName())
860	path := toolPath.GetPath()
861	if path == "" {
862		path = "NOT_USED"
863	}
864	path = fmt.Sprintf("path = \"%s\"", path)
865	return createObject("tool_path", []string{name, path}, depth)
866}
867
868func getMakeVariables(crosstool *crosstoolpb.CrosstoolRelease,
869	cToolchainIdentifiers map[string]CToolchainIdentifier, depth int) string {
870	var res []string
871	makeVariablesToIds := make(map[string][]string)
872	for _, toolchain := range crosstool.GetToolchain() {
873		makeVariables := parseMakeVariables(toolchain.GetMakeVariable(), depth)
874		makeVariablesToIds[makeVariables] = append(
875			makeVariablesToIds[makeVariables],
876			toolchain.GetToolchainIdentifier())
877	}
878	res = append(res,
879		getAssignmentStatement(
880			"make_variables",
881			makeVariablesToIds,
882			crosstool,
883			cToolchainIdentifiers,
884			depth,
885			/* isPlainString= */ false,
886			/* shouldFail= */ true))
887	return strings.Join(res, "\n")
888}
889
890func parseMakeVariables(makeVariables []*crosstoolpb.MakeVariable, depth int) string {
891	var res []string
892	for _, makeVariable := range makeVariables {
893		res = append(res, parseMakeVariable(makeVariable, depth+1))
894	}
895	return makeStringArr(res, depth /* isPlainString= */, false)
896}
897
898func parseMakeVariable(makeVariable *crosstoolpb.MakeVariable, depth int) string {
899	name := fmt.Sprintf("name = \"%s\"", makeVariable.GetName())
900	value := fmt.Sprintf("value = \"%s\"", makeVariable.GetValue())
901	return createObject("make_variable", []string{name, value}, depth)
902}
903
904func parseTools(tools []*crosstoolpb.CToolchain_Tool, depth int) string {
905	var res []string
906	for _, tool := range tools {
907		res = append(res, parseTool(tool, depth+1))
908	}
909	return makeStringArr(res, depth /* isPlainString= */, false)
910}
911
912func parseTool(tool *crosstoolpb.CToolchain_Tool, depth int) string {
913	toolPath := "path = \"NOT_USED\""
914	if tool.GetToolPath() != "" {
915		toolPath = fmt.Sprintf("path = \"%s\"", tool.GetToolPath())
916	}
917	fields := []string{toolPath}
918	if len(tool.GetWithFeature()) != 0 {
919		withFeatures := "with_features = " + parseWithFeatureSets(tool.GetWithFeature(), depth+1)
920		fields = append(fields, withFeatures)
921	}
922	if len(tool.GetExecutionRequirement()) != 0 {
923		executionRequirements := "execution_requirements = " +
924			makeStringArr(tool.GetExecutionRequirement(), depth+1 /* isPlainString= */, true)
925		fields = append(fields, executionRequirements)
926	}
927	return createObject("tool", fields, depth)
928}
929
930func parseEnvEntries(envEntries []*crosstoolpb.CToolchain_EnvEntry, depth int) string {
931	var res []string
932	for _, envEntry := range envEntries {
933		res = append(res, parseEnvEntry(envEntry, depth+1))
934	}
935	return makeStringArr(res, depth /* isPlainString= */, false)
936}
937
938func parseEnvEntry(envEntry *crosstoolpb.CToolchain_EnvEntry, depth int) string {
939	key := fmt.Sprintf("key = \"%s\"", envEntry.GetKey())
940	value := fmt.Sprintf("value = \"%s\"", envEntry.GetValue())
941	return createObject("env_entry", []string{key, value}, depth)
942}
943
944func parseWithFeatureSets(withFeatureSets []*crosstoolpb.CToolchain_WithFeatureSet,
945	depth int) string {
946	var res []string
947	for _, withFeature := range withFeatureSets {
948		res = append(res, parseWithFeatureSet(withFeature, depth+1))
949	}
950	return makeStringArr(res, depth /* isPlainString= */, false)
951}
952
953func parseWithFeatureSet(withFeature *crosstoolpb.CToolchain_WithFeatureSet,
954	depth int) string {
955	var fields []string
956	if len(withFeature.GetFeature()) != 0 {
957		features := "features = " +
958			makeStringArr(withFeature.GetFeature(), depth+1 /* isPlainString= */, true)
959		fields = append(fields, features)
960	}
961	if len(withFeature.GetNotFeature()) != 0 {
962		notFeatures := "not_features = " +
963			makeStringArr(withFeature.GetNotFeature(), depth+1 /* isPlainString= */, true)
964		fields = append(fields, notFeatures)
965	}
966	return createObject("with_feature_set", fields, depth)
967}
968
969func parseEnvSets(envSets []*crosstoolpb.CToolchain_EnvSet, depth int) string {
970	var res []string
971	for _, envSet := range envSets {
972		envSetString := parseEnvSet(envSet, depth+1)
973		res = append(res, envSetString)
974	}
975	return makeStringArr(res, depth /* isPlainString= */, false)
976}
977
978func parseEnvSet(envSet *crosstoolpb.CToolchain_EnvSet, depth int) string {
979	actionsStatement := processActions(envSet.GetAction(), depth)
980	actions := "actions = " + strings.Join(actionsStatement, " +\n"+getTabs(depth+2))
981	fields := []string{actions}
982	if len(envSet.GetEnvEntry()) != 0 {
983		envEntries := "env_entries = " + parseEnvEntries(envSet.GetEnvEntry(), depth+1)
984		fields = append(fields, envEntries)
985	}
986	if len(envSet.GetWithFeature()) != 0 {
987		withFeatures := "with_features = " + parseWithFeatureSets(envSet.GetWithFeature(), depth+1)
988		fields = append(fields, withFeatures)
989	}
990	return createObject("env_set", fields, depth)
991}
992
993func parseFeatureSets(featureSets []*crosstoolpb.CToolchain_FeatureSet, depth int) string {
994	var res []string
995	for _, featureSet := range featureSets {
996		res = append(res, parseFeatureSet(featureSet, depth+1))
997	}
998	return makeStringArr(res, depth /* isPlainString= */, false)
999}
1000
1001func parseFeatureSet(featureSet *crosstoolpb.CToolchain_FeatureSet, depth int) string {
1002	features := "features = " +
1003		makeStringArr(featureSet.GetFeature(), depth+1 /* isPlainString= */, true)
1004	return createObject("feature_set", []string{features}, depth)
1005}
1006
1007// Takes in a list of string elements and returns a string that represents
1008// an array :
1009//     [
1010//         "element1",
1011//         "element2",
1012//     ]
1013// The isPlainString argument tells us whether the input elements should be
1014// treated as string (eg, flags), or not (eg, variable names)
1015func makeStringArr(arr []string, depth int, isPlainString bool) string {
1016	if len(arr) == 0 {
1017		return "[]"
1018	}
1019	var escapedArr []string
1020	for _, el := range arr {
1021		if isPlainString {
1022			escapedArr = append(escapedArr, strings.Replace(el, "\"", "\\\"", -1))
1023		} else {
1024			escapedArr = append(escapedArr, el)
1025		}
1026	}
1027	addQuote := ""
1028	if isPlainString {
1029		addQuote = "\""
1030	}
1031	singleLine := "[" + addQuote + strings.Join(escapedArr, addQuote+", "+addQuote) + addQuote + "]"
1032	if len(singleLine) < 60 {
1033		return singleLine
1034	}
1035	return "[\n" +
1036		getTabs(depth+1) +
1037		addQuote +
1038		strings.Join(escapedArr, addQuote+",\n"+getTabs(depth+1)+addQuote) +
1039		addQuote +
1040		",\n" +
1041		getTabs(depth) +
1042		"]"
1043}
1044
1045// Returns a string that represents a value assignment
1046// (eg if ctx.attr.cpu == "linux":
1047//         compiler = "llvm"
1048//     elif ctx.attr.cpu == "windows":
1049//         compiler = "mingw"
1050//     else:
1051//         fail("Unreachable")
1052func getAssignmentStatement(field string, valToIds map[string][]string,
1053	crosstool *crosstoolpb.CrosstoolRelease,
1054	toCToolchainIdentifier map[string]CToolchainIdentifier,
1055	depth int, isPlainString, shouldFail bool) string {
1056	var b bytes.Buffer
1057	if len(valToIds) <= 1 {
1058		// if there is only one possible value for this field, we don't need if statements
1059		for val := range valToIds {
1060			if val != "None" && isPlainString {
1061				val = "\"" + val + "\""
1062			}
1063			b.WriteString(fmt.Sprintf("%s%s = %s\n", getTabs(depth), field, val))
1064			break
1065		}
1066	} else {
1067		first := true
1068		var keys []string
1069		for k := range valToIds {
1070			keys = append(keys, k)
1071		}
1072		sort.Strings(keys)
1073		for _, value := range keys {
1074			ids := valToIds[value]
1075			branch := "elif"
1076			if first {
1077				branch = "if"
1078			}
1079			b.WriteString(
1080				getIfStatement(branch, ids, field, value,
1081					toCToolchainIdentifier, depth, isPlainString))
1082			first = false
1083		}
1084		if shouldFail {
1085			b.WriteString(
1086				fmt.Sprintf(
1087					"%selse:\n%sfail(\"Unreachable\")\n",
1088					getTabs(depth), getTabs(depth+1)))
1089		} else {
1090			b.WriteString(
1091				fmt.Sprintf(
1092					"%selse:\n%s%s = None\n",
1093					getTabs(depth), getTabs(depth+1), field))
1094		}
1095	}
1096	b.WriteString("\n")
1097	return b.String()
1098}
1099
1100func getCPUToCompilers(identifiers []CToolchainIdentifier) map[string][]string {
1101	res := make(map[string][]string)
1102	for _, identifier := range identifiers {
1103		if identifier.compiler != "" {
1104			res[identifier.cpu] = append(res[identifier.cpu], identifier.compiler)
1105		}
1106	}
1107	return res
1108}
1109
1110func getIfStatement(ifOrElseIf string, identifiers []string, field, val string,
1111	toCToolchainIdentifier map[string]CToolchainIdentifier, depth int,
1112	isPlainString bool) string {
1113	usedStmts := make(map[string]bool)
1114	if val != "None" && isPlainString {
1115		val = "\"" + val + "\""
1116	}
1117	var cToolchainIdentifiers []CToolchainIdentifier
1118	for _, value := range toCToolchainIdentifier {
1119		cToolchainIdentifiers = append(cToolchainIdentifiers, value)
1120	}
1121	cpuToCompilers := getCPUToCompilers(cToolchainIdentifiers)
1122	countCpus := make(map[string]int)
1123	var conditions []string
1124	for _, id := range identifiers {
1125		identifier := toCToolchainIdentifier[id]
1126		stmt := getConditionStatementForCToolchainIdentifier(identifier)
1127		if _, ok := usedStmts[stmt]; !ok {
1128			conditions = append(conditions, stmt)
1129			usedStmts[stmt] = true
1130			if identifier.compiler != "" {
1131				countCpus[identifier.cpu]++
1132			}
1133		}
1134	}
1135
1136	var compressedConditions []string
1137	usedStmtsOptimized := make(map[string]bool)
1138	for _, id := range identifiers {
1139		identifier := toCToolchainIdentifier[id]
1140		var stmt string
1141		if _, ok := countCpus[identifier.cpu]; ok {
1142			if countCpus[identifier.cpu] == len(cpuToCompilers[identifier.cpu]) {
1143				stmt = getConditionStatementForCToolchainIdentifier(
1144					CToolchainIdentifier{cpu: identifier.cpu, compiler: ""})
1145			} else {
1146				stmt = getConditionStatementForCToolchainIdentifier(identifier)
1147			}
1148		} else {
1149			stmt = getConditionStatementForCToolchainIdentifier(identifier)
1150		}
1151		if _, ok := usedStmtsOptimized[stmt]; !ok {
1152			compressedConditions = append(compressedConditions, stmt)
1153			usedStmtsOptimized[stmt] = true
1154		}
1155	}
1156
1157	sort.Strings(compressedConditions)
1158	val = strings.Join(strings.Split(val, "\n"+getTabs(depth)), "\n"+getTabs(depth+1))
1159	return fmt.Sprintf(`%s%s %s:
1160%s%s = %s
1161`, getTabs(depth),
1162		ifOrElseIf,
1163		"("+strings.Join(compressedConditions, "\n"+getTabs(depth+1)+"or ")+")",
1164		getTabs(depth+1),
1165		field,
1166		val)
1167}
1168
1169func getToolchainIdentifiers(crosstool *crosstoolpb.CrosstoolRelease) []string {
1170	var res []string
1171	for _, toolchain := range crosstool.GetToolchain() {
1172		res = append(res, toolchain.GetToolchainIdentifier())
1173	}
1174	return res
1175}
1176
1177func getHostSystemNames(crosstool *crosstoolpb.CrosstoolRelease) []string {
1178	var res []string
1179	for _, toolchain := range crosstool.GetToolchain() {
1180		res = append(res, toolchain.GetHostSystemName())
1181	}
1182	return res
1183}
1184
1185func getTargetSystemNames(crosstool *crosstoolpb.CrosstoolRelease) []string {
1186	var res []string
1187	for _, toolchain := range crosstool.GetToolchain() {
1188		res = append(res, toolchain.GetTargetSystemName())
1189	}
1190	return res
1191}
1192
1193func getTargetCpus(crosstool *crosstoolpb.CrosstoolRelease) []string {
1194	var res []string
1195	for _, toolchain := range crosstool.GetToolchain() {
1196		res = append(res, toolchain.GetTargetCpu())
1197	}
1198	return res
1199}
1200
1201func getTargetLibcs(crosstool *crosstoolpb.CrosstoolRelease) []string {
1202	var res []string
1203	for _, toolchain := range crosstool.GetToolchain() {
1204		res = append(res, toolchain.GetTargetLibc())
1205	}
1206	return res
1207}
1208
1209func getCompilers(crosstool *crosstoolpb.CrosstoolRelease) []string {
1210	var res []string
1211	for _, toolchain := range crosstool.GetToolchain() {
1212		res = append(res, toolchain.GetCompiler())
1213	}
1214	return res
1215}
1216
1217func getAbiVersions(crosstool *crosstoolpb.CrosstoolRelease) []string {
1218	var res []string
1219	for _, toolchain := range crosstool.GetToolchain() {
1220		res = append(res, toolchain.GetAbiVersion())
1221	}
1222	return res
1223}
1224
1225func getAbiLibcVersions(crosstool *crosstoolpb.CrosstoolRelease) []string {
1226	var res []string
1227	for _, toolchain := range crosstool.GetToolchain() {
1228		res = append(res, toolchain.GetAbiLibcVersion())
1229	}
1230	return res
1231}
1232
1233func getCcTargetOss(crosstool *crosstoolpb.CrosstoolRelease) []string {
1234	var res []string
1235	for _, toolchain := range crosstool.GetToolchain() {
1236		targetOS := "None"
1237		if toolchain.GetCcTargetOs() != "" {
1238			targetOS = toolchain.GetCcTargetOs()
1239		}
1240		res = append(res, targetOS)
1241	}
1242	return res
1243}
1244
1245func getBuiltinSysroots(crosstool *crosstoolpb.CrosstoolRelease) []string {
1246	var res []string
1247	for _, toolchain := range crosstool.GetToolchain() {
1248		sysroot := "None"
1249		if toolchain.GetBuiltinSysroot() != "" {
1250			sysroot = toolchain.GetBuiltinSysroot()
1251		}
1252		res = append(res, sysroot)
1253	}
1254	return res
1255}
1256
1257func getMappedStringValuesToIdentifiers(identifiers, fields []string) map[string][]string {
1258	res := make(map[string][]string)
1259	for i := range identifiers {
1260		res[fields[i]] = append(res[fields[i]], identifiers[i])
1261	}
1262	return res
1263}
1264
1265func getReturnStatement() string {
1266	return `
1267    out = ctx.actions.declare_file(ctx.label.name)
1268    ctx.actions.write(out, "Fake executable")
1269    return [
1270        cc_common.create_cc_toolchain_config_info(
1271            ctx = ctx,
1272            features = features,
1273            action_configs = action_configs,
1274            artifact_name_patterns = artifact_name_patterns,
1275            cxx_builtin_include_directories = cxx_builtin_include_directories,
1276            toolchain_identifier = toolchain_identifier,
1277            host_system_name = host_system_name,
1278            target_system_name = target_system_name,
1279            target_cpu = target_cpu,
1280            target_libc = target_libc,
1281            compiler = compiler,
1282            abi_version = abi_version,
1283            abi_libc_version = abi_libc_version,
1284            tool_paths = tool_paths,
1285            make_variables = make_variables,
1286            builtin_sysroot = builtin_sysroot,
1287            cc_target_os = cc_target_os
1288        ),
1289        DefaultInfo(
1290            executable = out,
1291        ),
1292    ]
1293`
1294}
1295
1296// Transform writes a cc_toolchain_config rule functionally equivalent to the
1297// CROSSTOOL file.
1298func Transform(crosstool *crosstoolpb.CrosstoolRelease) (string, error) {
1299	var b bytes.Buffer
1300
1301	cToolchainIdentifiers := toolchainToCToolchainIdentifier(crosstool)
1302
1303	toolchainToFeatures, featureNameToFeature, err := getFeatures(crosstool)
1304	if err != nil {
1305		return "", err
1306	}
1307
1308	toolchainToActions, actionNameToAction, err := getActions(crosstool)
1309	if err != nil {
1310		return "", err
1311	}
1312
1313	header := getCcToolchainConfigHeader()
1314	if _, err := b.WriteString(header); err != nil {
1315		return "", err
1316	}
1317
1318	loadActionsStmt := getLoadActionsStmt()
1319	if _, err := b.WriteString(loadActionsStmt); err != nil {
1320		return "", err
1321	}
1322
1323	implHeader := getImplHeader()
1324	if _, err := b.WriteString(implHeader); err != nil {
1325		return "", err
1326	}
1327
1328	stringFields := []string{
1329		"toolchain_identifier",
1330		"host_system_name",
1331		"target_system_name",
1332		"target_cpu",
1333		"target_libc",
1334		"compiler",
1335		"abi_version",
1336		"abi_libc_version",
1337		"cc_target_os",
1338		"builtin_sysroot",
1339	}
1340
1341	for _, stringField := range stringFields {
1342		stmt := getStringStatement(crosstool, cToolchainIdentifiers, stringField, 1)
1343		if _, err := b.WriteString(stmt); err != nil {
1344			return "", err
1345		}
1346	}
1347
1348	listsOfActions := []string{
1349		"all_compile_actions",
1350		"all_cpp_compile_actions",
1351		"preprocessor_compile_actions",
1352		"codegen_compile_actions",
1353		"all_link_actions",
1354	}
1355
1356	for _, listOfActions := range listsOfActions {
1357		actions := getListOfActions(listOfActions, 1)
1358		if _, err := b.WriteString(actions); err != nil {
1359			return "", err
1360		}
1361	}
1362
1363	actionConfigDeclaration := getActionConfigsDeclaration(
1364		crosstool, cToolchainIdentifiers, actionNameToAction, 1)
1365	if _, err := b.WriteString(actionConfigDeclaration); err != nil {
1366		return "", err
1367	}
1368
1369	actionConfigStatement := getActionConfigsStmt(
1370		cToolchainIdentifiers, toolchainToActions, 1)
1371	if _, err := b.WriteString(actionConfigStatement); err != nil {
1372		return "", err
1373	}
1374
1375	featureDeclaration := getFeaturesDeclaration(
1376		crosstool, cToolchainIdentifiers, featureNameToFeature, 1)
1377	if _, err := b.WriteString(featureDeclaration); err != nil {
1378		return "", err
1379	}
1380
1381	featuresStatement := getFeaturesStmt(
1382		cToolchainIdentifiers, toolchainToFeatures, 1)
1383	if _, err := b.WriteString(featuresStatement); err != nil {
1384		return "", err
1385	}
1386
1387	includeDirectories := getStringArr(
1388		crosstool, cToolchainIdentifiers, "cxx_builtin_include_directories", 1)
1389	if _, err := b.WriteString(includeDirectories); err != nil {
1390		return "", err
1391	}
1392
1393	artifactNamePatterns := getArtifactNamePatterns(
1394		crosstool, cToolchainIdentifiers, 1)
1395	if _, err := b.WriteString(artifactNamePatterns); err != nil {
1396		return "", err
1397	}
1398
1399	makeVariables := getMakeVariables(crosstool, cToolchainIdentifiers, 1)
1400	if _, err := b.WriteString(makeVariables); err != nil {
1401		return "", err
1402	}
1403
1404	toolPaths := getToolPaths(crosstool, cToolchainIdentifiers, 1)
1405	if _, err := b.WriteString(toolPaths); err != nil {
1406		return "", err
1407	}
1408
1409	if _, err := b.WriteString(getReturnStatement()); err != nil {
1410		return "", err
1411	}
1412
1413	rule := getRule(cToolchainIdentifiers, getCompilers(crosstool))
1414	if _, err := b.WriteString(rule); err != nil {
1415		return "", err
1416	}
1417
1418	return b.String(), nil
1419}
1420