...

Source file src/github.com/bazelbuild/buildtools/labels/labels.go

Documentation: github.com/bazelbuild/buildtools/labels

     1  /*
     2   * Copyright 2020 Google LLC
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     https://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  // Package labels contains helper functions for working with labels.
    18  package labels
    19  
    20  import (
    21  	"bytes"
    22  	"path"
    23  	"strings"
    24  )
    25  
    26  // Label represents a Bazel target label.
    27  type Label struct {
    28  	Repository string // Repository of the target, can be empty if the target belongs to the current repository
    29  	Package    string // Package of a target, can be empty for top packages
    30  	Target     string // Name of the target, should be always non-empty
    31  }
    32  
    33  // Format returns a string representation of a label. It's always absolute but
    34  // the target name is omitted if it's equal to the package directory, e.g.
    35  // "//package/foo:foo" is formatted as "//package/foo".
    36  func (l Label) Format() string {
    37  	b := new(bytes.Buffer)
    38  	if l.Repository != "" {
    39  		b.WriteString("@")
    40  		b.WriteString(l.Repository)
    41  	}
    42  	if l.Repository == l.Target && l.Package == "" {
    43  		return b.String()
    44  	}
    45  	b.WriteString("//")
    46  	b.WriteString(l.Package)
    47  	if l.Target != path.Base(l.Package) {
    48  		b.WriteString(":")
    49  		b.WriteString(l.Target)
    50  	}
    51  	return b.String()
    52  }
    53  
    54  // FormatRelative returns a string representation of a label relative to `pkg`
    55  // (relative label if it represents a target in the same package, absolute otherwise)
    56  func (l Label) FormatRelative(pkg string) string {
    57  	if l.Repository != "" || pkg != l.Package {
    58  		// External repository or different package
    59  		return l.Format()
    60  	}
    61  	return ":" + l.Target
    62  }
    63  
    64  // Parse parses an absolute Bazel label (eg. //devtools/buildozer:rule)
    65  // and returns the corresponding Label object.
    66  func Parse(target string) Label {
    67  	label := Label{}
    68  	if strings.HasPrefix(target, "@") {
    69  		target = strings.TrimLeft(target, "@")
    70  		parts := strings.SplitN(target, "/", 2)
    71  		if len(parts) == 1 {
    72  			// "@foo" -> @foo//:foo
    73  			return Label{target, "", target}
    74  		}
    75  		label.Repository = parts[0]
    76  		target = "/" + parts[1]
    77  	}
    78  	parts := strings.SplitN(target, ":", 2)
    79  	parts[0] = strings.TrimPrefix(parts[0], "//")
    80  	label.Package = parts[0]
    81  	if len(parts) == 2 && parts[1] != "" {
    82  		label.Target = parts[1]
    83  	} else if !strings.HasPrefix(target, "//") {
    84  		// Maybe not really a label, store everything in Target
    85  		label.Target = target
    86  		label.Package = ""
    87  	} else {
    88  		// "//absolute/pkg" -> "absolute/pkg", "pkg"
    89  		label.Target = path.Base(parts[0])
    90  	}
    91  	return label
    92  }
    93  
    94  // ParseRelative parses a label `input` which may be absolute or relative.
    95  // If it's relative then it's considered to belong to `pkg`
    96  func ParseRelative(input, pkg string) Label {
    97  	if !strings.HasPrefix(input, "@") && !strings.HasPrefix(input, "//") {
    98  		return Label{Package: pkg, Target: strings.TrimLeft(input, ":")}
    99  	}
   100  	return Parse(input)
   101  }
   102  
   103  // Shorten rewrites labels to use the canonical form (the form
   104  // recommended by build-style).
   105  // "//foo/bar:bar" => "//foo/bar", or ":bar" if the label belongs to pkg
   106  func Shorten(input, pkg string) string {
   107  	if !strings.HasPrefix(input, "//") && !strings.HasPrefix(input, "@") {
   108  		// It doesn't look like a long label, so we preserve it.
   109  		// Maybe it's not a label at all, e.g. a filename.
   110  		return input
   111  	}
   112  	label := Parse(input)
   113  	return label.FormatRelative(pkg)
   114  }
   115  
   116  // Equal returns true if label1 and label2 are equal. The function
   117  // takes care of the optional ":" prefix and differences between long-form
   118  // labels and local labels (relative to pkg).
   119  func Equal(label1, label2, pkg string) bool {
   120  	return ParseRelative(label1, pkg) == ParseRelative(label2, pkg)
   121  }
   122  

View as plain text