/* Copyright 2017 The Bazel Authors. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rule import ( "sort" "strings" bzl "github.com/bazelbuild/buildtools/build" ) // PlatformConstraint represents a constraint_setting target for a particular // OS/arch combination. // // DEPRECATED: do not use outside language/go. type PlatformConstraint struct { Platform ConstraintPrefix string } func (p PlatformConstraint) String() string { pStr := p.Platform.String() if pStr == "" { return "" } return p.ConstraintPrefix + pStr } // PlatformStrings contains a set of strings associated with a buildable // target in a package. This is used to store source file names, // import paths, and flags. // // Strings are stored in four sets: generic strings, OS-specific strings, // arch-specific strings, and OS-and-arch-specific strings. A string may not // be duplicated within a list or across sets; however, a string may appear // in more than one list within a set (e.g., in "linux" and "windows" within // the OS set). Strings within each list should be sorted, though this may // not be relied upon. // // DEPRECATED: do not use outside language/go. This type is Go-specific and // should be moved to the Go extension. type PlatformStrings struct { // Generic is a list of strings not specific to any platform. Generic []string // OS is a map from an OS constraint to OS-specific strings. OS map[string][]string // Arch is a map from an architecture constraint to // architecture-specific strings. Arch map[string][]string // Platform is a map from platform constraints to OS and // architecture-specific strings. Platform map[PlatformConstraint][]string } // HasExt returns whether this set contains a file with the given extension. func (ps *PlatformStrings) HasExt(ext string) bool { return ps.firstExtFile(ext) != "" } func (ps *PlatformStrings) IsEmpty() bool { return len(ps.Generic) == 0 && len(ps.OS) == 0 && len(ps.Arch) == 0 && len(ps.Platform) == 0 } // Flat returns all the strings in the set, sorted and de-duplicated. func (ps *PlatformStrings) Flat() []string { unique := make(map[string]struct{}) for _, s := range ps.Generic { unique[s] = struct{}{} } for _, ss := range ps.OS { for _, s := range ss { unique[s] = struct{}{} } } for _, ss := range ps.Arch { for _, s := range ss { unique[s] = struct{}{} } } for _, ss := range ps.Platform { for _, s := range ss { unique[s] = struct{}{} } } flat := make([]string, 0, len(unique)) for s := range unique { flat = append(flat, s) } sort.Strings(flat) return flat } func (ps *PlatformStrings) firstExtFile(ext string) string { for _, f := range ps.Generic { if strings.HasSuffix(f, ext) { return f } } for _, fs := range ps.OS { for _, f := range fs { if strings.HasSuffix(f, ext) { return f } } } for _, fs := range ps.Arch { for _, f := range fs { if strings.HasSuffix(f, ext) { return f } } } for _, fs := range ps.Platform { for _, f := range fs { if strings.HasSuffix(f, ext) { return f } } } return "" } // Map applies a function that processes individual strings to the strings // in "ps" and returns a new PlatformStrings with the result. Empty strings // returned by the function are dropped. func (ps *PlatformStrings) Map(f func(s string) (string, error)) (PlatformStrings, []error) { var errors []error mapSlice := func(ss []string) ([]string, error) { rs := make([]string, 0, len(ss)) for _, s := range ss { if r, err := f(s); err != nil { errors = append(errors, err) } else if r != "" { rs = append(rs, r) } } return rs, nil } result, _ := ps.MapSlice(mapSlice) return result, errors } // MapSlice applies a function that processes slices of strings to the strings // in "ps" and returns a new PlatformStrings with the results. func (ps *PlatformStrings) MapSlice(f func([]string) ([]string, error)) (PlatformStrings, []error) { var errors []error mapSlice := func(ss []string) []string { rs, err := f(ss) if err != nil { errors = append(errors, err) return nil } return rs } mapStringMap := func(m map[string][]string) map[string][]string { if m == nil { return nil } rm := make(map[string][]string) for k, ss := range m { ss = mapSlice(ss) if len(ss) > 0 { rm[k] = ss } } if len(rm) == 0 { return nil } return rm } mapPlatformMap := func(m map[PlatformConstraint][]string) map[PlatformConstraint][]string { if m == nil { return nil } rm := make(map[PlatformConstraint][]string) for k, ss := range m { ss = mapSlice(ss) if len(ss) > 0 { rm[k] = ss } } if len(rm) == 0 { return nil } return rm } result := PlatformStrings{ Generic: mapSlice(ps.Generic), OS: mapStringMap(ps.OS), Arch: mapStringMap(ps.Arch), Platform: mapPlatformMap(ps.Platform), } return result, errors } func (ps PlatformStrings) BzlExpr() bzl.Expr { var pieces []bzl.Expr if len(ps.Generic) > 0 { pieces = append(pieces, ExprFromValue(ps.Generic)) } if len(ps.OS) > 0 { pieces = append(pieces, platformStringsOSArchDictExpr(ps.OS)) } if len(ps.Arch) > 0 { pieces = append(pieces, platformStringsOSArchDictExpr(ps.Arch)) } if len(ps.Platform) > 0 { pieces = append(pieces, platformStringsPlatformDictExpr(ps.Platform)) } if len(pieces) == 0 { return &bzl.ListExpr{} } else if len(pieces) == 1 { return pieces[0] } else { e := pieces[0] if list, ok := e.(*bzl.ListExpr); ok { list.ForceMultiLine = true } for _, piece := range pieces[1:] { e = &bzl.BinaryExpr{X: e, Y: piece, Op: "+"} } return e } } func platformStringsOSArchDictExpr(m map[string][]string) bzl.Expr { s := make(SelectStringListValue) for key, value := range m { s[key] = value } s["//conditions:default"] = nil return s.BzlExpr() } func platformStringsPlatformDictExpr(m map[PlatformConstraint][]string) bzl.Expr { s := make(SelectStringListValue) for key, value := range m { s[key.String()] = value } s["//conditions:default"] = nil return s.BzlExpr() }