Support workspace builds in build-webkit
https://bugs.webkit.org/show_bug.cgi?id=235550

On Apple platforms, `build-webkit --use-workspace` now builds using WebKit.xcworkspace and
XCBuild. This complements the `USE_WORKSPACE=YES` Make flag added in
https://commits.webkit.org/246232@main.

At this time, there is no guarantee of correctness when building with USE_WORKSPACE=YES, and
workspace builds are not checked by builders.

Patch by Elliott Williams <emw@apple.com> on 2022-01-26
Reviewed by Alexey Proskuryakov.

* Scripts/build-webkit: Add --use-workspace flag, correct platform hints in $usage.
* Scripts/webkitdirs.pm: Remove canUseXCBuild, the flag it powered is no longer used.
(buildXCodeWorkspace): Added.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@288632 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Tools/ChangeLog b/Tools/ChangeLog
index a245941..4f4a76b 100644
--- a/Tools/ChangeLog
+++ b/Tools/ChangeLog
@@ -1,3 +1,21 @@
+2022-01-26  Elliott Williams  <emw@apple.com>
+
+        Support workspace builds in build-webkit
+        https://bugs.webkit.org/show_bug.cgi?id=235550
+
+        On Apple platforms, `build-webkit --use-workspace` now builds using WebKit.xcworkspace and
+        XCBuild. This complements the `USE_WORKSPACE=YES` Make flag added in
+        https://commits.webkit.org/246232@main.
+
+        At this time, there is no guarantee of correctness when building with USE_WORKSPACE=YES, and
+        workspace builds are not checked by builders.
+
+        Reviewed by Alexey Proskuryakov.
+
+        * Scripts/build-webkit: Add --use-workspace flag, correct platform hints in $usage.
+        * Scripts/webkitdirs.pm: Remove canUseXCBuild, the flag it powered is no longer used.
+        (buildXCodeWorkspace): Added.
+
 2022-01-26  Philippe Normand  <pnormand@igalia.com>
 
         [GTK] C++20 warnings in TestConsoleMessage
diff --git a/Tools/Scripts/build-webkit b/Tools/Scripts/build-webkit
index 1e3db5a..b9ac724 100755
--- a/Tools/Scripts/build-webkit
+++ b/Tools/Scripts/build-webkit
@@ -75,6 +75,7 @@
 my $noExperimentalFeatures = 0;
 my $ltoMode = "default";
 my $xcbuild = undef;
+my $useWorkspace = undef;
 my $startTime = time();
 my $archs32bit = 0;
 my $skipLibraryUpdate = 0;
@@ -103,13 +104,13 @@
 my $programName = basename($0);
 my $usage = <<EOF;
 Usage: $programName [options] [options to pass to build system]
-  --help                            Show this help message
-  --verbose                         Show verbose build output
+  -h, --help                        Show this help message
+  -v, --verbose                     Show verbose build output
   --clean                           Cleanup the build directory
   --generate-project-only           Only generate project files
   --debug                           Compile with Debug configuration
   --release                         Compile with Release configuration
-  --sdk=<sdk>                       Use a specific Xcode SDK (iOS and Mac only)
+  --sdk=<sdk>                       Use a specific Xcode SDK (Apple platforms only)
   --ios-device                      Use "iphoneos.internal" SDK if installed, else "iphoneos" SDK (iOS only)
   --device                          DEPRECATED alias of --ios-device
   --ios-simulator                   Use "iphonesimulator.internal" SDK if installed, else "iphonesimulator" SDK (iOS only)
@@ -119,9 +120,10 @@
   --watchos-device                  Use "watchos.internal" SDK if installed, else "watchos" SDK (watchOS only)
   --watchos-simulator               Use "watchsimulator" (watchOS only)
   --coverage                        Enable code coverage support (Mac only)
-  --analyze                         Enable static anaylsis (iOS and Mac only)
+  --analyze                         Enable static anaylsis (Apple platforms only)
   --lto-mode=<mode>                 Set Link Time Optimization mode (full, thin, or none) (LLVM only)
-  --[no-]xcbuild                    Force the use of XCBuild or not
+  --[no-]xcbuild                    DEPRECATED use of XCBuild or not
+  --[no-]use-workspace              Use WebKit.xcworkspace and XCBuild (Apple platforms only)
 
   --ftw                             Build the FTW Windows port
   --gtk                             Build the GTK+ port
@@ -147,7 +149,7 @@
 EOF
 
 my %options = (
-    'help' => \$showHelp,
+    'h|help' => \$showHelp,
     'v|verbose' => \$verbose,
     'clean' => \$clean,
     'install-headers=s' => \$installHeaders,
@@ -162,6 +164,7 @@
     'no-experimental-features' => \$noExperimentalFeatures,
     'lto-mode=s' => \$ltoMode,
     'xcbuild!' => \$xcbuild,
+    'use-workspace!' => \$useWorkspace,
     'skip-library-update' => \$skipLibraryUpdate,
     'use-ccache!' => \$useCCache,
 );
@@ -222,13 +225,13 @@
 
 if (isAppleCocoaWebKit()) {
     push @options, XcodeOptions();
-
-    # Temporarily disable default use of XCBuild until issues with it are ironed out.
-    #if ((not defined $xcbuild or $xcbuild) and canUseXCBuild()) {
-    if ($xcbuild and canUseXCBuild()) {
+    if ($xcbuild) {
+        # Deprecated: Eventually, all XCBuild invocations will build through the workspace.
         push @options, "-UseNewBuildSystem=YES";
-    } else {
+    } elsif (!$useWorkspace) {
         push @options, "-UseNewBuildSystem=NO";
+    } else {
+        die "Error: --use-workspace and --no-xcbuild are not compatible. Workspaces builds require XCBuild.\n";
     }
 
     sub option($$)
@@ -243,30 +246,33 @@
         push @options, $option unless $option eq "";
     }
 
-    # ANGLE and libwebrtc must come before WebCore
-    splice @projects, 0, 0, ("Source/ThirdParty/ANGLE");
-    # if (not $archs32bit and (portName() eq Mac or portName() eq iOS or portName() eq watchOS)) {
-    if (portName() eq Mac or portName() eq iOS) {
-        splice @projects, 0, 0, ("Source/ThirdParty/libwebrtc");
+    # In workspaces, build order is determined by XCBuild.
+    if (!$useWorkspace) {
+        # ANGLE and libwebrtc must come before WebCore
+        splice @projects, 0, 0, ("Source/ThirdParty/ANGLE");
+        # if (not $archs32bit and (portName() eq Mac or portName() eq iOS or portName() eq watchOS)) {
+        if (portName() eq Mac or portName() eq iOS) {
+            splice @projects, 0, 0, ("Source/ThirdParty/libwebrtc");
+        }
+
+        push @projects, ("Source/WebKit");
+
+        if (!isEmbeddedWebKit()) {
+            push @projects, ("Tools/MiniBrowser");
+
+            # WebInspectorUI must come after JavaScriptCore and WebCore but before WebKit and WebKit2
+            my $webKitIndex = first { $projects[$_] eq "Source/WebKitLegacy" } 0..$#projects;
+            splice(@projects, $webKitIndex, 0, "Source/WebInspectorUI");
+        }
+
+        if (isAppleMacWebKit()) {
+            push @projects, ("Tools/lldb/lldbWebKitTester");
+        }
+
+        # Build Tools needed for Apple ports (except for tvOS)
+        push @projects, ("Tools/DumpRenderTree", "Tools/WebKitTestRunner", "Source/ThirdParty/gtest", "Tools/TestWebKitAPI");
     }
 
-    push @projects, ("Source/WebKit");
-
-    if (!isEmbeddedWebKit()) {
-        push @projects, ("Tools/MiniBrowser");
-
-        # WebInspectorUI must come after JavaScriptCore and WebCore but before WebKit and WebKit2
-        my $webKitIndex = first { $projects[$_] eq "Source/WebKitLegacy" } 0..$#projects;
-        splice(@projects, $webKitIndex, 0, "Source/WebInspectorUI");
-    }
-
-    if (isAppleMacWebKit()) {
-        push @projects, ("Tools/lldb/lldbWebKitTester");
-    }
-
-    # Build Tools needed for Apple ports (except for tvOS)
-    push @projects, ("Tools/DumpRenderTree", "Tools/WebKitTestRunner", "Source/ThirdParty/gtest", "Tools/TestWebKitAPI");
-
 } elsif (isWinCairo() && !$skipLibraryUpdate) {
     (system("python Tools/Scripts/update-webkit-wincairo-libs.py") == 0) or die;
 } elsif (isAppleWinWebKit() && !$skipLibraryUpdate) {
@@ -339,26 +345,34 @@
 } elsif (isAppleCocoaWebKit() && !isCMakeBuild()) {
     exit 0 if isGenerateProjectOnly();
 
+    my @local_options = @options;
+    push @local_options, XcodeCoverageSupportOptions() if $coverageSupport;
+    push @local_options, XcodeStaticAnalyzerOption() if $shouldRunStaticAnalyzer;
+    push @local_options, "WK_LTO_MODE=$ltoMode" if ($ltoMode ne "default");
+
     # Build, and abort if the build fails.
-    for my $dir (@projects) {
-        chdir $dir or die;
-        $result = 0;
-
-        my $project = basename($dir);
-        
-        my @local_options = @options;
-        push @local_options, XcodeCoverageSupportOptions() if $coverageSupport;
-        push @local_options, XcodeStaticAnalyzerOption() if $shouldRunStaticAnalyzer;
-        push @local_options, "WK_LTO_MODE=$ltoMode" if ($ltoMode ne "default");
-        my $projectPath = $project =~ /gtest/ ? "xcode/gtest" : $project;
-        $result = buildXCodeProject($projectPath, $clean, @local_options, @ARGV);
-
-        # Various build* calls above may change the CWD.
-        chdirWebKit();
-
+    if ($useWorkspace) {
+        my $scheme = $onlyWebKitProject ? "WebKitLegacy" : "All Modules";
+        $result = buildXCodeWorkspace("WebKit.xcworkspace", $scheme, $clean, @local_options, @ARGV);
         if (exitStatus($result)) {
             exit exitStatus($result);
         }
+    } else {
+        for my $dir (@projects) {
+            chdir $dir or die;
+            $result = 0;
+
+            my $project = basename($dir);
+            my $projectPath = $project =~ /gtest/ ? "xcode/gtest" : $project;
+            $result = buildXCodeProject($projectPath, $clean, @local_options, @ARGV);
+
+            # Various build* calls above may change the CWD.
+            chdirWebKit();
+
+            if (exitStatus($result)) {
+                exit exitStatus($result);
+            }
+        }
     }
 
     if (isInspectorFrontend()) {
diff --git a/Tools/Scripts/webkitdirs.pm b/Tools/Scripts/webkitdirs.pm
index 48e14e5..d5604b3 100755
--- a/Tools/Scripts/webkitdirs.pm
+++ b/Tools/Scripts/webkitdirs.pm
@@ -1079,12 +1079,6 @@
     return "RUN_CLANG_STATIC_ANALYZER=YES";
 }
 
-sub canUseXCBuild()
-{
-    determineXcodeVersion();
-    return (eval "v$xcodeVersion" ge v11.4)
-}
-
 my $passedConfiguration;
 my $searchedForPassedConfiguration;
 sub determinePassedConfiguration
@@ -2113,6 +2107,15 @@
     return system "xcodebuild", "-project", "$project.xcodeproj", @extraOptions;
 }
 
+sub buildXCodeWorkspace($$$@)
+{
+    my ($workspace, $scheme, $clean, @extraOptions) = @_;
+    if ($clean) {
+        push @extraOptions, "clean";
+    }
+    return system "xcodebuild", "-workspace", $workspace, "-scheme", $scheme, @extraOptions;
+}
+
 sub getVisualStudioToolset()
 {
     if (isPlayStation()) {