Merge pull request #190 from ddunbar/rdar35179142

Export LLBUILD_TASK_ID to subprocesses.
diff --git a/lib/BuildSystem/LaneBasedExecutionQueue.cpp b/lib/BuildSystem/LaneBasedExecutionQueue.cpp
index 50197a8..2fb2c2c 100644
--- a/lib/BuildSystem/LaneBasedExecutionQueue.cpp
+++ b/lib/BuildSystem/LaneBasedExecutionQueue.cpp
@@ -11,13 +11,15 @@
 //===----------------------------------------------------------------------===//
 
 #include "llbuild/BuildSystem/BuildExecutionQueue.h"
-#include "llbuild/BuildSystem/CommandResult.h"
+
+#include "POSIXEnvironment.h"
 
 #include "llbuild/Basic/LLVM.h"
 #include "llbuild/Basic/PlatformUtility.h"
 #include "llbuild/Basic/Tracing.h"
 
 #include "llbuild/BuildSystem/BuildDescription.h"
+#include "llbuild/BuildSystem/CommandResult.h"
 
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/Hashing.h"
@@ -48,14 +50,6 @@
 using namespace llbuild;
 using namespace llbuild::buildsystem;
 
-namespace std {
-  template<> struct hash<llvm::StringRef> {
-    size_t operator()(const StringRef& value) const {
-      return size_t(hash_value(value));
-    }
-  };
-}
-
 namespace {
 
 struct LaneBasedExecutionQueueJobContext {
@@ -358,50 +352,31 @@
     args[argsStorage.size()] = nullptr;
 
     // Form the complete environment.
-    std::vector<std::string> envStorage;
-    std::vector<const char*> env;
-    const char* const* envp = nullptr;
+    //
+    // NOTE: We construct the environment in order of precedence, so
+    // overridden keys should be defined first.
+    POSIXEnvironment posixEnv;
 
-    // If no additional environment is supplied, use the base environment.
-    if (environment.empty()) {
-      envp = this->environment;
-    } else {
-      // Inherit the base environment, if desired.
-      if (inheritEnvironment) {
-        std::unordered_set<StringRef> overriddenKeys{};
-        // Compute the set of strings which are overridden in the process
-        // environment.
-        for (const auto& entry: environment) {
-          overriddenKeys.insert(entry.first);
-        }
-
-        // Form the complete environment by adding the base key value pairs
-        // which are not overridden, then adding all of the overridden ones.
-        for (const char* const* p = this->environment; *p != nullptr; ++p) {
-          // Find the key.
-          auto key = StringRef(*p).split('=').first;
-          if (!overriddenKeys.count(key)) {
-            env.emplace_back(*p);
-          }
-        }
+    // Export a task ID to subprocesses.
+    //
+    // We currently only export the lane ID, but eventually will export a unique
+    // task ID for SR-6053.
+    posixEnv.setIfMissing("LLBUILD_TASK_ID", Twine(context.laneNumber).str());
+                          
+    // Add the requested environment.
+    for (const auto& entry: environment) {
+      posixEnv.setIfMissing(entry.first, entry.second);
+    }
+      
+    // Inherit the base environment, if desired.
+    //
+    // FIXME: This involves a lot of redundant allocation, currently. We could
+    // cache this for the common case of a directly inherited environment.
+    if (inheritEnvironment) {
+      for (const char* const* p = this->environment; *p != nullptr; ++p) {
+        auto pair = StringRef(*p).split('=');
+        posixEnv.setIfMissing(pair.first, pair.second);
       }
-
-      // Add the requested environment.
-      for (const auto& entry: environment) {
-        SmallString<256> assignment;
-        assignment += entry.first;
-        assignment += '=';
-        assignment += entry.second;
-        assignment += '\0';
-        envStorage.emplace_back(assignment.str());
-      }
-      // We must do this in a second pass, once the entries are guaranteed not
-      // to move.
-      for (const auto& entry: envStorage) {
-        env.emplace_back(entry.c_str());
-      }
-      env.emplace_back(nullptr);
-      envp = env.data();
     }
 
     // Resolve the executable path, if necessary.
@@ -428,7 +403,7 @@
       if (!wasCancelled) {
         if (posix_spawn(&pid, args[0], /*file_actions=*/&fileActions,
                         /*attrp=*/&attributes, const_cast<char**>(args.data()),
-                        const_cast<char* const*>(envp)) != 0) {
+                        const_cast<char* const*>(posixEnv.getEnvp())) != 0) {
           getDelegate().commandProcessHadError(
               context.job.getForCommand(), handle,
               Twine("unable to spawn process (") + strerror(errno) + ")");
diff --git a/lib/BuildSystem/POSIXEnvironment.h b/lib/BuildSystem/POSIXEnvironment.h
new file mode 100644
index 0000000..7963f9c
--- /dev/null
+++ b/lib/BuildSystem/POSIXEnvironment.h
@@ -0,0 +1,91 @@
+//===- POSIXEnvironment.h ---------------------------------------*- C++ -*-===//
+//
+// This source file is part of the Swift.org open source project
+//
+// Copyright (c) 2017 Apple Inc. and the Swift project authors
+// Licensed under Apache License v2.0 with Runtime Library Exception
+//
+// See http://swift.org/LICENSE.txt for license information
+// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLBUILD_BUILDSYSTEM_PROCESSENVIRONMENT_H
+#define LLBUILD_BUILDSYSTEM_PROCESSENVIRONMENT_H
+
+#include "llbuild/Basic/LLVM.h"
+
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Hashing.h"
+
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+namespace std {
+  template<> struct hash<llvm::StringRef> {
+    size_t operator()(const llvm::StringRef& value) const {
+      return size_t(hash_value(value));
+    }
+  };
+}
+
+namespace llbuild {
+namespace buildsystem {
+
+/// A helper class for constructing a POSIX-style environment.
+class POSIXEnvironment {
+  /// The actual environment, this is only populated once frozen.
+  std::vector<const char*> env;
+
+  /// The underlying string storage.
+  //
+  // FIXME: This is not efficient, we could store into a single allocation.
+  std::vector<std::string> envStorage;
+
+  /// The list of known keys in the environment.
+  std::unordered_set<StringRef> keys{};
+
+  /// Whether the environment pointer has been vended, and assignments can no
+  /// longer be mutated.
+  bool isFrozen = false;
+    
+public:
+  POSIXEnvironment() {}
+
+  /// Add a key to the environment, if missing.
+  ///
+  /// If the key has already been defined, it will **NOT** be inserted.
+  void setIfMissing(StringRef key, StringRef value) {
+    assert(!isFrozen);
+    if (keys.insert(key).second) {
+      llvm::SmallString<256> assignment;
+      assignment += key;
+      assignment += '=';
+      assignment += value;
+      assignment += '\0';
+      envStorage.emplace_back(assignment.str());
+    }
+  }
+
+  /// Get the envirnonment pointer.
+  ///
+  /// This pointer is only valid for the lifetime of the environment itself.
+  const char* const* getEnvp() {
+    isFrozen = true;
+
+    // Form the final environment.
+    env.clear();
+    for (const auto& entry: envStorage) {
+      env.emplace_back(entry.c_str());
+    }
+    env.emplace_back(nullptr);
+    return env.data();
+  }
+};
+
+}
+}
+
+#endif
diff --git a/llbuild.xcodeproj/project.pbxproj b/llbuild.xcodeproj/project.pbxproj
index f1e92b1..1bda707 100644
--- a/llbuild.xcodeproj/project.pbxproj
+++ b/llbuild.xcodeproj/project.pbxproj
@@ -129,6 +129,7 @@
 		E171538D1A0BF702004CD598 /* CorePerfTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = E171538C1A0BF702004CD598 /* CorePerfTests.mm */; };
 		E17440C31CE192FF0070A30C /* ShellUtility.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E17440C21CE192FF0070A30C /* ShellUtility.cpp */; };
 		E192E92F1E30014E00122F17 /* BuildValueTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E192E92E1E30014E00122F17 /* BuildValueTest.cpp */; };
+		E19880EB1FA256FC00E490FF /* POSIXEnvironmentTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E19880EA1FA256FB00E490FF /* POSIXEnvironmentTest.cpp */; };
 		E19D79921A15D9E6002604FB /* MakefileDepsParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E19D79911A15D9E6002604FB /* MakefileDepsParser.cpp */; };
 		E19D79951A15DA06002604FB /* MakefileDepsParserTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E19D79941A15DA06002604FB /* MakefileDepsParserTest.cpp */; };
 		E1A0B0FF1C971582006DA08F /* DependencyInfoParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E1A0B0FE1C971581006DA08F /* DependencyInfoParser.cpp */; };
@@ -765,6 +766,8 @@
 		E181D1451F7D90AC0015286C /* Tracing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Tracing.h; sourceTree = "<group>"; };
 		E182BE111ABA2B8D001840AD /* Compiler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Compiler.h; sourceTree = "<group>"; };
 		E192E92E1E30014E00122F17 /* BuildValueTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BuildValueTest.cpp; sourceTree = "<group>"; };
+		E19880E91FA256D900E490FF /* POSIXEnvironment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = POSIXEnvironment.h; sourceTree = "<group>"; };
+		E19880EA1FA256FB00E490FF /* POSIXEnvironmentTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = POSIXEnvironmentTest.cpp; sourceTree = "<group>"; };
 		E19C3FD51B98C1A70035E1AA /* tests */ = {isa = PBXFileReference; lastKnownFileType = folder; path = tests; sourceTree = "<group>"; };
 		E19D79911A15D9E6002604FB /* MakefileDepsParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MakefileDepsParser.cpp; sourceTree = "<group>"; };
 		E19D79931A15D9F5002604FB /* MakefileDepsParser.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MakefileDepsParser.h; sourceTree = "<group>"; };
@@ -1202,9 +1205,10 @@
 				C5740D081E03523100567DD8 /* BuildSystemFrontendTest.cpp */,
 				E1075ED61E4EA417007D52C6 /* BuildSystemTaskTests.cpp */,
 				E192E92E1E30014E00122F17 /* BuildValueTest.cpp */,
+				9DB0478B1DF9D3E2006CDF52 /* LaneBasedExecutionQueueTest.cpp */,
 				E1B3B9DA1E4D5A7A00DF1FBC /* MockBuildSystemDelegate.cpp */,
 				E1B3B9DB1E4D5A7A00DF1FBC /* MockBuildSystemDelegate.h */,
-				9DB0478B1DF9D3E2006CDF52 /* LaneBasedExecutionQueueTest.cpp */,
+				E19880EA1FA256FB00E490FF /* POSIXEnvironmentTest.cpp */,
 				9D0A6D7F1E1FFEA800BE636F /* TempDir.cpp */,
 				9D0A6D801E1FFEA800BE636F /* TempDir.h */,
 			);
@@ -1835,6 +1839,7 @@
 				E1FC67F81BB1F417004EBC54 /* BuildValue.cpp */,
 				E1AAD28F1BC65AB200F54680 /* ExternalCommand.cpp */,
 				E1066C071BC5ACAB00B892CE /* LaneBasedExecutionQueue.cpp */,
+				E19880E91FA256D900E490FF /* POSIXEnvironment.h */,
 				E1AAD2911BC65B5000F54680 /* SwiftTools.cpp */,
 			);
 			path = BuildSystem;
@@ -2658,6 +2663,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				E19880EB1FA256FC00E490FF /* POSIXEnvironmentTest.cpp in Sources */,
 				E192E92F1E30014E00122F17 /* BuildValueTest.cpp in Sources */,
 				9DB047C01DF9F592006CDF52 /* LaneBasedExecutionQueueTest.cpp in Sources */,
 				C5740D091E03523100567DD8 /* BuildSystemFrontendTest.cpp in Sources */,
diff --git a/tests/BuildSystem/Build/basic.llbuild b/tests/BuildSystem/Build/basic.llbuild
index 6110430..86f734b 100644
--- a/tests/BuildSystem/Build/basic.llbuild
+++ b/tests/BuildSystem/Build/basic.llbuild
@@ -13,6 +13,7 @@
 # CHECK-NOT: PATH=
 # CHECK-NEXT: /usr/bin/env
 # CHECK: ENV_KEY=ENV_VALUE_2
+# CHECK: LLBUILD_TASK_ID=0
 # CHECK: PATH=random-overridden-path
 # CHECK: cp 'input A' output
 
diff --git a/unittests/BuildSystem/CMakeLists.txt b/unittests/BuildSystem/CMakeLists.txt
index 6534b81..9ca7b1a 100644
--- a/unittests/BuildSystem/CMakeLists.txt
+++ b/unittests/BuildSystem/CMakeLists.txt
@@ -1,9 +1,10 @@
 add_llbuild_unittest(BuildSystemTests
-  BuildSystemTaskTests.cpp
   BuildSystemFrontendTest.cpp
+  BuildSystemTaskTests.cpp
   BuildValueTest.cpp
+  LaneBasedExecutionQueueTest.cpp
   MockBuildSystemDelegate.cpp
-  LaneBasedExecutionQueueTest
+  POSIXEnvironmentTest.cpp
   TempDir.cpp
   )
 
diff --git a/unittests/BuildSystem/POSIXEnvironmentTest.cpp b/unittests/BuildSystem/POSIXEnvironmentTest.cpp
new file mode 100644
index 0000000..2ed8fae
--- /dev/null
+++ b/unittests/BuildSystem/POSIXEnvironmentTest.cpp
@@ -0,0 +1,32 @@
+//===- unittests/BuildSystem/POSIXEnvironmentTest.cpp ---------------------===//
+//
+// This source file is part of the Swift.org open source project
+//
+// Copyright (c) 2017 Apple Inc. and the Swift project authors
+// Licensed under Apache License v2.0 with Runtime Library Exception
+//
+// See http://swift.org/LICENSE.txt for license information
+// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
+//
+//===----------------------------------------------------------------------===//
+
+#include "../../lib/BuildSystem/POSIXEnvironment.h"
+
+#include "gtest/gtest.h"
+
+using namespace llbuild;
+using namespace llbuild::buildsystem;
+
+namespace {
+  TEST(POSIXEnvironmentTest, basic) {
+    POSIXEnvironment env;
+    env.setIfMissing("a", "aValue");
+    env.setIfMissing("b", "bValue");
+    env.setIfMissing("a", "NOT HERE");
+
+    auto result = env.getEnvp();
+    EXPECT_EQ(StringRef(result[0]), "a=aValue");
+    EXPECT_EQ(StringRef(result[1]), "b=bValue");
+    EXPECT_EQ(result[2], nullptr);
+  }
+}