[iOS] Add application target as dependency of xctest module target.

Previously, clicking on the "Play" button near a test in the test
navigator doesn't automatically re-build the application target, so that
tests don't test the current state of the code, but the one when it was
last compiled.

This CL fixes the problem by adding corresponding application target as
a dependency of xctest module target.

BUG=705005

Review-Url: https://codereview.chromium.org/2779833002
Cr-Original-Commit-Position: refs/heads/master@{#460805}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: 611254763b178dd9a1680e1e107a09aefb1267cb
diff --git a/tools/gn/xcode_object.cc b/tools/gn/xcode_object.cc
index 9d574a5..b8c8502 100644
--- a/tools/gn/xcode_object.cc
+++ b/tools/gn/xcode_object.cc
@@ -244,6 +244,8 @@
       return "PBXAggregateTarget";
     case PBXBuildFileClass:
       return "PBXBuildFile";
+    case PBXContainerItemProxyClass:
+      return "PBXContainerItemProxy";
     case PBXFileReferenceClass:
       return "PBXFileReference";
     case PBXFrameworksBuildPhaseClass:
@@ -258,6 +260,8 @@
       return "PBXShellScriptBuildPhase";
     case PBXSourcesBuildPhaseClass:
       return "PBXSourcesBuildPhase";
+    case PBXTargetDependencyClass:
+      return "PBXTargetDependency";
     case XCBuildConfigurationClass:
       return "XCBuildConfiguration";
     case XCConfigurationListClass:
@@ -323,6 +327,11 @@
 
 PBXTarget::~PBXTarget() {}
 
+void PBXTarget::AddDependency(std::unique_ptr<PBXTargetDependency> dependency) {
+  DCHECK(dependency);
+  dependencies_.push_back(std::move(dependency));
+}
+
 std::string PBXTarget::Name() const {
   return name_;
 }
@@ -330,9 +339,10 @@
 void PBXTarget::Visit(PBXObjectVisitor& visitor) {
   PBXObject::Visit(visitor);
   configurations_->Visit(visitor);
-  for (const auto& build_phase : build_phases_) {
+  for (const auto& dependency : dependencies_)
+    dependency->Visit(visitor);
+  for (const auto& build_phase : build_phases_)
     build_phase->Visit(visitor);
-  }
 }
 
 // PBXAggregateTarget ---------------------------------------------------------
@@ -399,6 +409,37 @@
   out << "};\n";
 }
 
+// PBXContainerItemProxy ------------------------------------------------------
+PBXContainerItemProxy::PBXContainerItemProxy(const PBXProject* project,
+                                             const PBXTarget* target)
+    : project_(project), target_(target) {}
+
+PBXContainerItemProxy::~PBXContainerItemProxy() {}
+
+PBXObjectClass PBXContainerItemProxy::Class() const {
+  return PBXContainerItemProxyClass;
+}
+
+void PBXContainerItemProxy::Visit(PBXObjectVisitor& visitor) {
+  PBXObject::Visit(visitor);
+}
+
+std::string PBXContainerItemProxy::Name() const {
+  return "PBXContainerItemProxy";
+}
+
+void PBXContainerItemProxy::Print(std::ostream& out, unsigned indent) const {
+  const std::string indent_str(indent, '\t');
+  const IndentRules rules = {true, 0};
+  out << indent_str << Reference() << " = {";
+  PrintProperty(out, rules, "isa", ToString(Class()));
+  PrintProperty(out, rules, "containerPortal", project_);
+  PrintProperty(out, rules, "proxyType", 1u);
+  PrintProperty(out, rules, "remoteGlobalIDString", target_);
+  PrintProperty(out, rules, "remoteInfo", target_->Name());
+  out << indent_str << "};\n";
+}
+
 // PBXFileReference -----------------------------------------------------------
 
 PBXFileReference::PBXFileReference(const std::string& name,
@@ -604,7 +645,7 @@
   PrintProperty(out, rules, "buildConfigurationList", configurations_);
   PrintProperty(out, rules, "buildPhases", build_phases_);
   PrintProperty(out, rules, "buildRules", EmptyPBXObjectVector());
-  PrintProperty(out, rules, "dependencies", EmptyPBXObjectVector());
+  PrintProperty(out, rules, "dependencies", dependencies_);
   PrintProperty(out, rules, "name", name_);
   PrintProperty(out, rules, "productName", product_name_);
   PrintProperty(out, rules, "productReference", product_reference_);
@@ -843,6 +884,33 @@
   out << indent_str << "};\n";
 }
 
+PBXTargetDependency::PBXTargetDependency(
+    const PBXTarget* target,
+    std::unique_ptr<PBXContainerItemProxy> container_item_proxy)
+    : target_(target), container_item_proxy_(std::move(container_item_proxy)) {}
+
+PBXTargetDependency::~PBXTargetDependency() {}
+
+PBXObjectClass PBXTargetDependency::Class() const {
+  return PBXTargetDependencyClass;
+}
+std::string PBXTargetDependency::Name() const {
+  return "PBXTargetDependency";
+}
+void PBXTargetDependency::Visit(PBXObjectVisitor& visitor) {
+  PBXObject::Visit(visitor);
+  container_item_proxy_->Visit(visitor);
+}
+void PBXTargetDependency::Print(std::ostream& out, unsigned indent) const {
+  const std::string indent_str(indent, '\t');
+  const IndentRules rules = {false, indent + 1};
+  out << indent_str << Reference() << " = {\n";
+  PrintProperty(out, rules, "isa", ToString(Class()));
+  PrintProperty(out, rules, "target", target_);
+  PrintProperty(out, rules, "targetProxy", container_item_proxy_);
+  out << indent_str << "};\n";
+}
+
 // XCBuildConfiguration -------------------------------------------------------
 
 XCBuildConfiguration::XCBuildConfiguration(const std::string& name,
diff --git a/tools/gn/xcode_object.h b/tools/gn/xcode_object.h
index a6ab32a..c695dbb 100644
--- a/tools/gn/xcode_object.h
+++ b/tools/gn/xcode_object.h
@@ -33,6 +33,7 @@
   // Those values needs to stay sorted in alphabetic order.
   PBXAggregateTargetClass,
   PBXBuildFileClass,
+  PBXContainerItemProxyClass,
   PBXFileReferenceClass,
   PBXFrameworksBuildPhaseClass,
   PBXGroupClass,
@@ -40,6 +41,7 @@
   PBXProjectClass,
   PBXShellScriptBuildPhaseClass,
   PBXSourcesBuildPhaseClass,
+  PBXTargetDependencyClass,
   XCBuildConfigurationClass,
   XCConfigurationListClass,
 };
@@ -50,8 +52,9 @@
 
 class PBXAggregateTarget;
 class PBXBuildFile;
-class PBXFileReference;
 class PBXBuildPhase;
+class PBXContainerItemProxy;
+class PBXFileReference;
 class PBXFrameworksBuildPhase;
 class PBXGroup;
 class PBXNativeTarget;
@@ -60,6 +63,7 @@
 class PBXShellScriptBuildPhase;
 class PBXSourcesBuildPhase;
 class PBXTarget;
+class PBXTargetDependency;
 class XCBuildConfiguration;
 class XCConfigurationList;
 
@@ -122,13 +126,16 @@
             const PBXAttributes& attributes);
   ~PBXTarget() override;
 
-  // PXBObject implementation.
+  void AddDependency(std::unique_ptr<PBXTargetDependency> dependency);
+
+  // PBXObject implementation.
   std::string Name() const override;
   void Visit(PBXObjectVisitor& visitor) override;
 
  protected:
   std::unique_ptr<XCConfigurationList> configurations_;
   std::vector<std::unique_ptr<PBXBuildPhase>> build_phases_;
+  std::vector<std::unique_ptr<PBXTargetDependency>> dependencies_;
   PBXSourcesBuildPhase* source_build_phase_;
   std::string name_;
 
@@ -146,7 +153,7 @@
                      const PBXAttributes& attributes);
   ~PBXAggregateTarget() override;
 
-  // PXBObject implementation.
+  // PBXObject implementation.
   PBXObjectClass Class() const override;
   void Print(std::ostream& out, unsigned indent) const override;
 
@@ -163,7 +170,7 @@
                const CompilerFlags compiler_flag);
   ~PBXBuildFile() override;
 
-  // PXBObject implementation.
+  // PBXObject implementation.
   PBXObjectClass Class() const override;
   std::string Name() const override;
   void Print(std::ostream& out, unsigned indent) const override;
@@ -176,6 +183,25 @@
   DISALLOW_COPY_AND_ASSIGN(PBXBuildFile);
 };
 
+// PBXContainerItemProxy ------------------------------------------------------
+class PBXContainerItemProxy : public PBXObject {
+ public:
+  PBXContainerItemProxy(const PBXProject* project, const PBXTarget* target);
+  ~PBXContainerItemProxy() override;
+
+  // PBXObject implementation.
+  PBXObjectClass Class() const override;
+  std::string Name() const override;
+  void Visit(PBXObjectVisitor& visitor) override;
+  void Print(std::ostream& out, unsigned indent) const override;
+
+ private:
+  const PBXProject* project_;
+  const PBXTarget* target_;
+
+  DISALLOW_COPY_AND_ASSIGN(PBXContainerItemProxy);
+};
+
 // PBXFileReference -----------------------------------------------------------
 
 class PBXFileReference : public PBXObject {
@@ -372,6 +398,27 @@
   DISALLOW_COPY_AND_ASSIGN(PBXSourcesBuildPhase);
 };
 
+// PBXTargetDependency -----------------------------------------------------
+class PBXTargetDependency : public PBXObject {
+ public:
+  PBXTargetDependency(
+      const PBXTarget* target,
+      std::unique_ptr<PBXContainerItemProxy> container_item_proxy);
+  ~PBXTargetDependency() override;
+
+  // PBXObject implementation.
+  PBXObjectClass Class() const override;
+  std::string Name() const override;
+  void Visit(PBXObjectVisitor& visitor) override;
+  void Print(std::ostream& out, unsigned indent) const override;
+
+ private:
+  const PBXTarget* target_;
+  std::unique_ptr<PBXContainerItemProxy> container_item_proxy_;
+
+  DISALLOW_COPY_AND_ASSIGN(PBXTargetDependency);
+};
+
 // XCBuildConfiguration -------------------------------------------------------
 
 class XCBuildConfiguration : public PBXObject {
diff --git a/tools/gn/xcode_writer.cc b/tools/gn/xcode_writer.cc
index 8b96db4..c41e4f1 100644
--- a/tools/gn/xcode_writer.cc
+++ b/tools/gn/xcode_writer.cc
@@ -33,6 +33,7 @@
 
 using TargetToFileList = std::unordered_map<const Target*, Target::FileList>;
 using TargetToTarget = std::unordered_map<const Target*, const Target*>;
+using TargetToPBXTarget = std::unordered_map<const Target*, PBXTarget*>;
 
 const char kEarlGreyFileNameIdentifier[] = "egtest.mm";
 const char kXCTestFileNameIdentifier[] = "xctest.mm";
@@ -460,6 +461,8 @@
   DCHECK_EQ(xctest_application_targets.size(),
             xctest_files_per_application_target.size());
 
+  TargetToPBXTarget bundle_target_to_pbxtarget;
+
   std::string build_path;
   std::unique_ptr<base::Environment> env(base::Environment::Create());
 
@@ -505,6 +508,8 @@
             target->bundle_data().product_type(),
             GetBuildScript(target->label().name(), ninja_extra_args, env.get()),
             extra_attributes);
+        bundle_target_to_pbxtarget.insert(
+            std::make_pair(target, native_target));
 
         if (!IsXCTestModuleTarget(target))
           continue;
@@ -535,6 +540,31 @@
     }
   }
 
+  // Add corresponding application target as dependency of xctest module target
+  // so that application target is re-compiled when compiling xctest module
+  // target.
+  for (const Target* target : targets) {
+    if (target->output_type() != Target::CREATE_BUNDLE)
+      continue;
+    if (!IsXCTestModuleTarget(target))
+      continue;
+
+    const Target* application_target =
+        FindXCTestApplicationTarget(target, targets);
+    PBXTarget* application_pbxtarget =
+        bundle_target_to_pbxtarget[application_target];
+    DCHECK(application_pbxtarget);
+    PBXTarget* xctest_module_pbxtarget = bundle_target_to_pbxtarget[target];
+    DCHECK(xctest_module_pbxtarget);
+
+    std::unique_ptr<PBXContainerItemProxy> container_item_proxy(
+        new PBXContainerItemProxy(main_project.get(), application_pbxtarget));
+    std::unique_ptr<PBXTargetDependency> dependency(new PBXTargetDependency(
+        application_pbxtarget, std::move(container_item_proxy)));
+
+    xctest_module_pbxtarget->AddDependency(std::move(dependency));
+  }
+
   projects_.push_back(std::move(main_project));
 }