PageRenderTime 46ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/src/com/facebook/buck/parser/BuildTargetParser.java

https://gitlab.com/smartether/buck
Java | 169 lines | 121 code | 16 blank | 32 comment | 12 complexity | a6946bea783446e23a6c7cbaef7e4231 MD5 | raw file
  1. /*
  2. * Copyright 2012-present Facebook, Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may
  5. * not use this file except in compliance with the License. You may obtain
  6. * a copy of the License at
  7. *
  8. * http://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, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations
  14. * under the License.
  15. */
  16. package com.facebook.buck.parser;
  17. import com.facebook.buck.model.BuildTarget;
  18. import com.facebook.buck.model.FlavorParser;
  19. import com.facebook.buck.model.InternalFlavor;
  20. import com.facebook.buck.model.UnflavoredBuildTarget;
  21. import com.facebook.buck.rules.CellPathResolver;
  22. import com.google.common.base.Preconditions;
  23. import com.google.common.base.Splitter;
  24. import com.google.common.collect.ImmutableSet;
  25. import com.google.common.collect.Interner;
  26. import com.google.common.collect.Interners;
  27. import java.util.HashSet;
  28. import java.util.List;
  29. import java.util.Optional;
  30. import java.util.Set;
  31. public class BuildTargetParser {
  32. /**
  33. * The BuildTargetParser is stateless, so this single instance can be shared.
  34. */
  35. public static final BuildTargetParser INSTANCE = new BuildTargetParser();
  36. private static final String BUILD_RULE_PREFIX = "//";
  37. private static final String BUILD_RULE_SEPARATOR = ":";
  38. private static final Splitter BUILD_RULE_SEPARATOR_SPLITTER = Splitter.on(BUILD_RULE_SEPARATOR);
  39. private static final Set<String> INVALID_BASE_NAME_PARTS = ImmutableSet.of(".", "..");
  40. private final Interner<BuildTarget> flavoredTargetCache = Interners.newWeakInterner();
  41. private final FlavorParser flavorParser = new FlavorParser();
  42. private BuildTargetParser() {
  43. // this is stateless. There's no need to do anything other than grab the instance needed.
  44. }
  45. /**
  46. * @param buildTargetName either a fully-qualified name or relative to the {@link BuildTargetPatternParser}.
  47. * For example, inside {@code first-party/orca/orcaapp/BUCK}, which can be obtained by
  48. * calling {@code ParseContext.forBaseName("first-party/orca/orcaapp")},
  49. * {@code //first-party/orca/orcaapp:assets} and {@code :assets} refer to the same target.
  50. * However, from the command line the context is obtained by calling
  51. * {@link BuildTargetPatternParser#fullyQualified()} and relative names are
  52. * not recognized.
  53. * @param buildTargetPatternParser how targets should be interpreted, such in the context of a
  54. * specific build file or only as fully-qualified names (as is the case for targets from the
  55. * command line).
  56. */
  57. public BuildTarget parse(
  58. String buildTargetName,
  59. BuildTargetPatternParser<?> buildTargetPatternParser,
  60. CellPathResolver cellNames) {
  61. if (buildTargetName.endsWith(BUILD_RULE_SEPARATOR) &&
  62. !buildTargetPatternParser.isWildCardAllowed()) {
  63. throw new BuildTargetParseException(
  64. String.format("%s cannot end with a colon", buildTargetName));
  65. }
  66. Optional<String> givenCellName = Optional.empty();
  67. String targetAfterCell = buildTargetName;
  68. if (buildTargetName.contains(BUILD_RULE_PREFIX) &&
  69. !buildTargetName.startsWith(BUILD_RULE_PREFIX)) {
  70. int slashIndex = buildTargetName.indexOf(BUILD_RULE_PREFIX);
  71. givenCellName = Optional.of(buildTargetName.substring(0, slashIndex));
  72. targetAfterCell = buildTargetName.substring(slashIndex);
  73. }
  74. if (givenCellName.isPresent() && givenCellName.get().isEmpty()) {
  75. throw new BuildTargetParseException("Cell name must not be empty.");
  76. }
  77. List<String> parts = BUILD_RULE_SEPARATOR_SPLITTER.splitToList(targetAfterCell);
  78. if (parts.size() != 2) {
  79. throw new BuildTargetParseException(String.format(
  80. "%s must contain exactly one colon (found %d)", buildTargetName, parts.size() - 1));
  81. }
  82. String baseName =
  83. parts.get(0).isEmpty() ? buildTargetPatternParser.getBaseName() : parts.get(0);
  84. String shortName = parts.get(1);
  85. Iterable<String> flavorNames = new HashSet<>();
  86. int hashIndex = shortName.indexOf("#");
  87. if (hashIndex != -1 && hashIndex < shortName.length()) {
  88. flavorNames = flavorParser.parseFlavorString(shortName.substring(hashIndex + 1));
  89. shortName = shortName.substring(0, hashIndex);
  90. }
  91. Preconditions.checkNotNull(baseName);
  92. // On Windows, baseName may contain backslashes, which are not permitted by BuildTarget.
  93. baseName = baseName.replace("\\", "/");
  94. checkBaseName(baseName, buildTargetName);
  95. UnflavoredBuildTarget.Builder unflavoredBuilder =
  96. UnflavoredBuildTarget.builder()
  97. .setBaseName(baseName)
  98. .setShortName(shortName)
  99. // Set the cell path correctly. Because the cellNames comes from the owning cell we can
  100. // be sure that if this doesn't throw an exception the target cell is visible to the
  101. // owning cell.
  102. .setCellPath(cellNames.getCellPath(givenCellName))
  103. // We are setting the cell name so we can print it later
  104. .setCell(givenCellName);
  105. UnflavoredBuildTarget unflavoredBuildTarget = unflavoredBuilder.build();
  106. BuildTarget.Builder builder = BuildTarget.builder(unflavoredBuildTarget);
  107. for (String flavor : flavorNames) {
  108. builder.addFlavors(InternalFlavor.of(flavor));
  109. }
  110. return flavoredTargetCache.intern(builder.build());
  111. }
  112. protected static void checkBaseName(String baseName, String buildTargetName) {
  113. if (baseName.equals(BUILD_RULE_PREFIX)) {
  114. return;
  115. }
  116. if (!baseName.startsWith(BUILD_RULE_PREFIX)) {
  117. throw new BuildTargetParseException(
  118. String.format(
  119. "Path in %s must start with %s",
  120. buildTargetName,
  121. BUILD_RULE_PREFIX));
  122. }
  123. String baseNamePath = baseName.substring(BUILD_RULE_PREFIX.length());
  124. if (baseNamePath.startsWith("/")) {
  125. throw new BuildTargetParseException(
  126. String.format(
  127. "Build target path should start with an optional cell name, then // and then a " +
  128. "relative directory name, not an absolute directory path (found %s)",
  129. buildTargetName
  130. ));
  131. }
  132. for (String baseNamePart : Splitter.on('/').split(baseNamePath)) {
  133. if ("".equals(baseNamePart)) {
  134. throw new BuildTargetParseException(
  135. String.format(
  136. "Build target path cannot contain // other than at the start " +
  137. "(or after a cell name) (found %s)",
  138. buildTargetName
  139. ));
  140. }
  141. if (INVALID_BASE_NAME_PARTS.contains(baseNamePart)) {
  142. throw new BuildTargetParseException(
  143. String.format(
  144. "Build target path cannot contain . or .. (found %s)",
  145. buildTargetName));
  146. }
  147. }
  148. }
  149. }