TBR: Update jiri-swift to support SyncbaseCore (and arbitrary projects)

Abstracted settings to a selected project. Project must always be
specified (we can later build all by default if useful) using the
-project flag (possible values are SyncbaseCore or VanadiumCore).

Updated header parsing/generator for SyncbaseCore.

Change-Id: If4636629515decb78db50d3b9195fb5a500c841c
diff --git a/jiri-swift/build_cgo.go b/jiri-swift/build_cgo.go
index 7056de0..9cd83e6 100644
--- a/jiri-swift/build_cgo.go
+++ b/jiri-swift/build_cgo.go
@@ -9,6 +9,7 @@
 	"fmt"
 	"os"
 	"path"
+	"path/filepath"
 	"strings"
 	"text/template"
 
@@ -51,6 +52,7 @@
 	sh.Pushd(flagBuildDirCgo)
 
 	for _, targetArch := range targetArchs {
+		cleanOldCompiledFiles(jirix, targetArch)
 		compileCgo(jirix, targetArch)
 		installCgoBinary(jirix, targetArch)
 	}
@@ -62,11 +64,46 @@
 	return generateSingleHeader(jirix, targetArchs[0])
 }
 
+func cleanOldCompiledFiles(jirix *jiri.X, targetArch string) {
+	d := filepath.Join(jirix.Root, "release/go/pkg", "darwin_"+targetArch, "v.io")
+	if !pathExists(d) {
+		verbose(jirix, "Previously built go binaries & headers directory doesn't exist, nothing to remove: %v\n", d)
+		return
+	}
+	sanityCheckDir(d)
+	verbose(jirix, "Removing compiled go files and headers in path %v\n", d)
+	if err := os.RemoveAll(d); err != nil {
+		panic(fmt.Sprint("Unable to remove old compiled files:", err))
+	}
+}
+
 func compileCgo(jirix *jiri.X, targetArch string) {
 	targetFlag := targetArch + "-ios"
-	verbose(jirix, "Building for target %v with build mode %v in dir %v\n", targetFlag, flagBuildMode, flagBuildDirCgo)
-	sh.Cmd("jiri", "go", "-target", targetFlag, "build", "-buildmode="+flagBuildMode, "-tags", "ios", "v.io/x/swift/main").Run()
-	sh.Cmd("jiri", "go", "-target", targetFlag, "install", "-buildmode="+flagBuildMode, "-tags", "ios", "v.io/x/swift/main").Run()
+	verbose(jirix, "Building for project %v target %v with build mode %v in dir %v\n", selectedProject.name, targetFlag, flagBuildMode, flagBuildDirCgo)
+	// Create the binary
+	bp := buildBinaryPath(targetArch)
+	verbose(jirix, "Running jiri go -target %v build -buildmode=%v -tags ios -o %v %v\n", targetFlag, flagBuildMode, bp, selectedProject.mainPackage)
+	sh.Cmd("jiri", "go", "-target", targetFlag, "build", "-buildmode="+flagBuildMode, "-tags", "ios", "-o", bp, selectedProject.mainPackage).Run()
+	// If the package is simple enough it'll also generate a header -- we'll use the installed
+	// headers instead (as its more universal), so we can delete this generated header now if
+	// it exists.
+	b := strings.TrimSuffix(bp, filepath.Ext(bp))
+	os.RemoveAll(b + ".h")
+	// Now make sure the headers are created/generated in our go/pkg directory for a later step.
+	verbose(jirix, "Running jiri go -target %v install -buildmode=%v -tags ios %v\n", targetFlag, flagBuildMode, selectedProject.mainPackage)
+	sh.Cmd("jiri", "go", "-target", targetFlag, "install", "-buildmode="+flagBuildMode, "-tags", "ios", selectedProject.mainPackage).Run()
+}
+
+func buildBinaryPath(targetArch string) string {
+	bn := path.Join(flagBuildDirCgo, selectedProject.libraryBinaryName+"_"+targetArch)
+	switch flagBuildMode {
+	case buildModeArchive:
+		return bn + ".a"
+	case buildModeShared:
+		return bn + ".dylib"
+	default:
+		panic("Unknown build mode")
+	}
 }
 
 func installCgoBinary(jirix *jiri.X, targetArch string) {
@@ -77,15 +114,14 @@
 	var destLibPath string
 	switch flagBuildMode {
 	case buildModeArchive:
-		a := fmt.Sprintf("%v_%v.a", libraryBinaryName, targetArch)
+		a := fmt.Sprintf("%v_%v.a", selectedProject.libraryBinaryName, targetArch)
 		destLibPath = path.Join(swiftTargetDir, a)
-		sh.Cmd("mv", "main.a", destLibPath).Run()
+		sh.Cmd("mv", buildBinaryPath(targetArch), destLibPath).Run()
 	case buildModeShared:
-		dylib := fmt.Sprintf("%v_%v.dylib", libraryBinaryName, targetArch)
+		dylib := fmt.Sprintf("%v_%v.dylib", selectedProject.libraryBinaryName, targetArch)
 		destLibPath = path.Join(swiftTargetDir, dylib)
-		sh.Cmd("mv", "main", dylib).Run()
-		sh.Cmd("install_name_tool", "-id", "@loader_path/"+dylib, dylib).Run()
-		sh.Cmd("mv", dylib, destLibPath).Run()
+		sh.Cmd("mv", buildBinaryPath(targetArch), destLibPath).Run()
+		sh.Cmd("install_name_tool", "-id", "@loader_path/"+dylib, destLibPath).Run()
 	}
 	verbose(jirix, "Installed binary at %v\n", destLibPath)
 	verifyCgoBinaryArchOrPanic(destLibPath, targetArch)
@@ -94,13 +130,13 @@
 func copyCommonHeaders(jirix *jiri.X) {
 	verbose(jirix, "Copying common shared headers between Swift and Go\n")
 	// Take types.h and make it into go_types.h
-	sh.Cmd("cp", path.Join(jirix.Root, "release/go/src/v.io/x/swift/types.h"), path.Join(getSwiftTargetDir(jirix), "go_types.h")).Run()
+	sh.Cmd("cp", path.Join(jirix.Root, selectedProject.commonHeaderPath), path.Join(getSwiftTargetDir(jirix), "go_types.h")).Run()
 }
 
 func generateSingleHeader(jirix *jiri.X, targetArch string) error {
 	verbose(jirix, "Generating header for Swift\n")
 	// Load and parse all the headers
-	generatedHeadersDir := fmt.Sprintf("%v/release/go/pkg/darwin_%v/v.io/x", jirix.Root, targetArch)
+	generatedHeadersDir := fmt.Sprintf("%v/release/go/pkg/darwin_%v/%v", jirix.Root, targetArch, selectedProject.exportedHeadersPackageRoot)
 	generatedHeadersPaths := findHeadersUnderPath(generatedHeadersDir)
 	hdrs := cgoHeaders{}
 	for _, file := range generatedHeadersPaths {
diff --git a/jiri-swift/build_framework.go b/jiri-swift/build_framework.go
index aa886c6..5c55fda 100644
--- a/jiri-swift/build_framework.go
+++ b/jiri-swift/build_framework.go
@@ -18,13 +18,13 @@
 		// will fail otherwise.
 		return fmt.Errorf("Framework builds are always universal -- target must be all")
 	}
-	xcodeTarget := frameworkBinaryName
-	sh.Pushd(filepath.Join(jirix.Root, "release/swift/lib"))
+	xcodeTarget := selectedProject.frameworkBinaryName
+	sh.Pushd(filepath.Join(jirix.Root, "release/swift", selectedProject.directoryName))
 	// Make sure target directory exists
 	sh.Cmd("mkdir", "-p", flagOutDirSwift).Run()
 	// Clear out the old framework
 	sanityCheckDir(flagOutDirSwift)
-	targetPath := filepath.Join(flagOutDirSwift, frameworkName)
+	targetPath := filepath.Join(flagOutDirSwift, selectedProject.frameworkName)
 	verbose(jirix, "Building framework at %v\n", targetPath)
 	if pathExists(targetPath) {
 		verbose(jirix, "Removing old framework at %v\n", targetPath)
@@ -36,14 +36,14 @@
 }
 
 func buildUniversalFramework(jirix *jiri.X, xcodeTarget string) error {
-	fatBinaryPath := filepath.Join(flagOutDirSwift, frameworkName, frameworkBinaryName)
+	fatBinaryPath := filepath.Join(flagOutDirSwift, selectedProject.frameworkName, selectedProject.frameworkBinaryName)
 	didCopyFramework := false
 	for _, targetArch := range targetArchs {
 		buildDir, err := buildSingleFramework(jirix, xcodeTarget, targetArch)
 		if err != nil {
 			return err
 		}
-		builtFrameworkPath := filepath.Join(buildDir, frameworkName)
+		builtFrameworkPath := filepath.Join(buildDir, selectedProject.frameworkName)
 		if !didCopyFramework {
 			verbose(jirix, "Copying framework from %v to %v\n", builtFrameworkPath, flagOutDirSwift)
 			sh.Cmd("cp", "-r", builtFrameworkPath, flagOutDirSwift).Run()
@@ -51,8 +51,8 @@
 			continue
 		}
 		// Copy this architecture's swift modules
-		builtModulesPath := filepath.Join(builtFrameworkPath, "Modules", frameworkBinaryName+".swiftmodule")
-		targetModulesPath := filepath.Join(flagOutDirSwift, frameworkName, "Modules", frameworkBinaryName+".swiftmodule")
+		builtModulesPath := filepath.Join(builtFrameworkPath, "Modules", selectedProject.frameworkBinaryName+".swiftmodule")
+		targetModulesPath := filepath.Join(flagOutDirSwift, selectedProject.frameworkName, "Modules", selectedProject.frameworkBinaryName+".swiftmodule")
 		verbose(jirix, "Copying built modules from %v to %v\n", filepath.Join(builtModulesPath, "*"), targetModulesPath)
 		modules, err := filepath.Glob(filepath.Join(builtModulesPath, "*"))
 		if err != nil {
@@ -62,7 +62,7 @@
 			sh.Cmd("cp", "-f", module, targetModulesPath).Run()
 		}
 		// Inject the architecture binary
-		sh.Cmd("lipo", fatBinaryPath, filepath.Join(builtFrameworkPath, frameworkBinaryName), "-create", "-output", fatBinaryPath).Run()
+		sh.Cmd("lipo", fatBinaryPath, filepath.Join(builtFrameworkPath, selectedProject.frameworkBinaryName), "-create", "-output", fatBinaryPath).Run()
 	}
 	return nil
 }
diff --git a/jiri-swift/build_test.go b/jiri-swift/build_test.go
index 6138219..5b83dbc 100644
--- a/jiri-swift/build_test.go
+++ b/jiri-swift/build_test.go
@@ -20,11 +20,6 @@
 	"v.io/jiri/tool"
 )
 
-var (
-	checkExportedSymbols = []string{"swift_io_v_v23_V_nativeInitGlobal", "swift_io_v_v23_context_VContext_nativeWithCancel"}
-	checkSharedTypes     = []string{"SwiftByteArray", "SwiftByteArrayArray", "GoContextHandle"}
-)
-
 func resetVars() {
 	buildCgo = false
 	buildFramework = false
@@ -37,10 +32,12 @@
 	flagBuildMode = buildModeArchive
 	flagBuildDirCgo = ""
 	flagOutDirSwift = sh.MakeTempDir()
+	flagProject = ""
 	flagReleaseMode = false
 	flagTargetArch = targetArchAll
 
 	targetArchs = []string{} // gets set by parseBuildFlags()
+	selectedProject = nil
 	parseBuildFlags()
 }
 
@@ -87,6 +84,33 @@
 	return jirix
 }
 
+func TestParseProjectFlags(t *testing.T) {
+	resetVars()
+	if err := parseProjectFlag(); err == nil {
+		t.Errorf("Expected error when no project is set")
+	}
+	flagProject = "VanadiumCore"
+	if err := parseProjectFlag(); err != nil {
+		t.Errorf("Expected no error for VandiumCore")
+	}
+	flagProject = "vanadiumcore"
+	if err := parseProjectFlag(); err != nil {
+		t.Errorf("Expected no error for vanadiumcore")
+	}
+	flagProject = "SyncbaseCore"
+	if err := parseProjectFlag(); err != nil {
+		t.Errorf("Expected no error for SyncbaseCore")
+	}
+	flagProject = "syncbasecore"
+	if err := parseProjectFlag(); err != nil {
+		t.Errorf("Expected no error for syncbasecore")
+	}
+	flagProject = "Something else"
+	if err := parseProjectFlag(); err == nil {
+		t.Errorf("Expected error non-project name")
+	}
+}
+
 func TestParseBuildFlags(t *testing.T) {
 	resetVars()
 	// Test shared only working on amd64
@@ -168,42 +192,54 @@
 }
 
 func TestCgoBuildForSimulator64(t *testing.T) {
-	jirix := initForTest(t)
-	if err := testCgoBuildForArch(jirix, targetArchAmd64, buildModeArchive); err != nil {
-		t.Error(err)
-	}
-	if err := testCgoBuildForArch(jirix, targetArchAmd64, buildModeShared); err != nil {
-		t.Error(err)
+	for _, p := range projects {
+		jirix := initForTest(t)
+		if err := testCgoBuildForArch(jirix, p, targetArchAmd64, buildModeArchive); err != nil {
+			t.Error(err)
+		}
+		if err := testCgoBuildForArch(jirix, p, targetArchAmd64, buildModeShared); err != nil {
+			t.Error(err)
+		}
 	}
 }
 
 func TestCgoBuildForArm(t *testing.T) {
-	jirix := initForTest(t)
-	// Expect error for ARM currently as of Go 1.5
-	if err := testCgoBuildForArch(jirix, targetArchArm, buildModeArchive); err == nil {
-		t.Error("Expected error for building unsupported 32-bit arm")
+	for _, p := range projects {
+		jirix := initForTest(t)
+		// Expect error for ARM currently as of Go 1.5
+		if err := testCgoBuildForArch(jirix, p, targetArchArm, buildModeArchive); err == nil {
+			t.Error("Expected error for building unsupported 32-bit arm")
+		}
 	}
 }
 
 func TestCgoBuildForArm64(t *testing.T) {
-	jirix := initForTest(t)
-	if err := testCgoBuildForArch(jirix, targetArchArm64, buildModeArchive); err != nil {
-		t.Error(err)
+	for _, p := range projects {
+		jirix := initForTest(t)
+		if err := testCgoBuildForArch(jirix, p, targetArchArm64, buildModeArchive); err != nil {
+			t.Error(err)
+		}
 	}
 }
 
 func TestCgoBuildForAll(t *testing.T) {
-	jirix := initForTest(t)
-	if err := testCgoBuildForArch(jirix, targetArchAll, buildModeArchive); err != nil {
-		t.Error(err)
+	for _, p := range projects {
+		jirix := initForTest(t)
+		if err := testCgoBuildForArch(jirix, p, targetArchAll, buildModeArchive); err != nil {
+			t.Error(err)
+		}
 	}
 }
 
-func testCgoBuildForArch(jirix *jiri.X, arch string, buildMode string) error {
+func testCgoBuildForArch(jirix *jiri.X, p project, arch string, buildMode string) error {
 	resetVars()
 	buildCgo = true
 	flagBuildMode = buildMode
 	flagTargetArch = arch
+	flagProject = p.name
+	if err := parseProjectFlag(); err != nil {
+		return err
+	}
 	if err := parseBuildFlags(); err != nil {
 		return err
 	}
@@ -250,7 +286,7 @@
 }
 
 func cgoBinaryPath(jirix *jiri.X, arch string, buildMode string) (string, error) {
-	binaryPath := path.Join(getSwiftTargetDir(jirix), fmt.Sprintf("%v_%v", libraryBinaryName, arch))
+	binaryPath := path.Join(getSwiftTargetDir(jirix), fmt.Sprintf("%v_%v", selectedProject.libraryBinaryName, arch))
 	switch buildMode {
 	case buildModeArchive:
 		binaryPath = binaryPath + ".a"
@@ -277,7 +313,7 @@
 func verifyCgoBinaryExports(binaryPath string) error {
 	stdout := sh.Cmd("otool", "-l", binaryPath).Stdout()
 	// Test a couple of key functions to make sure we're getting our exports
-	for _, symbol := range checkExportedSymbols {
+	for _, symbol := range selectedProject.testCheckExportedSymbols {
 		if !strings.Contains(stdout, symbol) {
 			fmt.Errorf("Missing %v in %v export table", symbol, binaryPath)
 		}
@@ -295,7 +331,7 @@
 		return err
 	}
 	goTypes := string(bytes)
-	for _, typedef := range checkSharedTypes {
+	for _, typedef := range selectedProject.testCheckSharedTypes {
 		if !strings.Contains(goTypes, typedef) {
 			return fmt.Errorf("Missing shared typedef of %v in %v", typedef, goTypesPath)
 		}
@@ -313,7 +349,7 @@
 		return err
 	}
 	cgoExports := string(bytes)
-	for _, symbol := range checkExportedSymbols {
+	for _, symbol := range selectedProject.testCheckExportedSymbols {
 		if !strings.Contains(cgoExports, symbol) {
 			return fmt.Errorf("Missing symbol %v in %v", symbol, cgoExportsPath)
 		}
@@ -343,34 +379,41 @@
 }
 
 func TestUniversalFrameworkBuilds(t *testing.T) {
-	jirix := initForTest(t)
-	flagTargetArch = targetArchAll
-	if err := parseBuildFlags(); err != nil {
-		t.Error(err)
-		return
-	}
-	// Make sure VanadiumCore exports exist
-	if err := runBuildCgo(jirix); err != nil {
-		t.Error(err)
-		return
-	}
-	if err := runBuildFramework(jirix); err != nil {
-		t.Error(err)
-		return
-	}
-	binaryPath := filepath.Join(flagOutDirSwift, frameworkName, frameworkBinaryName)
-	if err := verifyCgoBinaryForIOS(binaryPath); err != nil {
-		t.Error(err)
-		return
-	}
-	for _, targetArch := range targetArchs {
-		appleArch, _ := appleArchFromGoArch(targetArch)
-		sh.Cmd("lipo", binaryPath, "-verify_arch", appleArch).Run()
-		if !pathExists(filepath.Join(flagOutDirSwift, frameworkName, "Modules", frameworkBinaryName+".swiftmodule", appleArch+".swiftdoc")) {
-			t.Errorf("Missing swift moduledoc for architecture %v", targetArch)
+	for _, p := range projects {
+		jirix := initForTest(t)
+		flagTargetArch = targetArchAll
+		flagProject = p.name
+		if err := parseProjectFlag(); err != nil {
+			t.Error(err)
+			return
 		}
-		if !pathExists(filepath.Join(flagOutDirSwift, frameworkName, "Modules", frameworkBinaryName+".swiftmodule", appleArch+".swiftmodule")) {
-			t.Errorf("Missing swift module for architecture %v", targetArch)
+		if err := parseBuildFlags(); err != nil {
+			t.Error(err)
+			return
+		}
+		// Make sure VanadiumCore exports exist
+		if err := runBuildCgo(jirix); err != nil {
+			t.Error(err)
+			return
+		}
+		if err := runBuildFramework(jirix); err != nil {
+			t.Error(err)
+			return
+		}
+		binaryPath := filepath.Join(flagOutDirSwift, selectedProject.frameworkName, selectedProject.frameworkBinaryName)
+		if err := verifyCgoBinaryForIOS(binaryPath); err != nil {
+			t.Error(err)
+			return
+		}
+		for _, targetArch := range targetArchs {
+			appleArch, _ := appleArchFromGoArch(targetArch)
+			sh.Cmd("lipo", binaryPath, "-verify_arch", appleArch).Run()
+			if !pathExists(filepath.Join(flagOutDirSwift, selectedProject.frameworkName, "Modules", selectedProject.frameworkBinaryName+".swiftmodule", appleArch+".swiftdoc")) {
+				t.Errorf("Missing swift moduledoc for architecture %v", targetArch)
+			}
+			if !pathExists(filepath.Join(flagOutDirSwift, selectedProject.frameworkName, "Modules", selectedProject.frameworkBinaryName+".swiftmodule", appleArch+".swiftmodule")) {
+				t.Errorf("Missing swift module for architecture %v", targetArch)
+			}
 		}
 	}
 }
diff --git a/jiri-swift/cgo_header.go b/jiri-swift/cgo_header.go
index 185d492..7db3c89 100644
--- a/jiri-swift/cgo_header.go
+++ b/jiri-swift/cgo_header.go
@@ -122,7 +122,12 @@
 	case strings.HasPrefix(line, "#line"):
 		// #line 19 "/Users/zinman/vanadium/release/go/src/v.io/x/swift/impl/google/rt/swift.go"
 		// ignore
+	case strings.HasPrefix(line, "#include") && strings.Contains(line, "lib.h"):
+		// SyncbaseCore's common import
+		// #import "lib.h"
+		// ignore
 	case strings.HasPrefix(line, "#import") && strings.Contains(line, "types.h"):
+		// VanadiumCore's common import
 		// #import "../../../types.h"
 		// ignore
 	case strings.HasPrefix(line, "#import"):
diff --git a/jiri-swift/doc.go b/jiri-swift/doc.go
index 27dcafd..d39af3c 100644
--- a/jiri-swift/doc.go
+++ b/jiri-swift/doc.go
@@ -6,7 +6,7 @@
 // DO NOT UPDATE MANUALLY
 
 /*
-Manages the build pipeline for the Swift framework, from CGO bindings to
+Manages the build pipeline for the Swift framework/app, from CGO bindings to
 fattening the binaries.
 
 Usage:
@@ -14,7 +14,7 @@
 
 The jiri swift commands are:
    build       Builds and installs the cgo wrapper, as well as the Swift
-               framework
+               framework/app
    clean       Removes generated cgo binaries and headers
    help        Display help for commands or topics
 
diff --git a/jiri-swift/swift.go b/jiri-swift/swift.go
index b063076..4f79b59 100644
--- a/jiri-swift/swift.go
+++ b/jiri-swift/swift.go
@@ -10,6 +10,7 @@
 import (
 	"fmt"
 	"runtime"
+	"strings"
 
 	"v.io/jiri"
 	"v.io/jiri/tool"
@@ -26,12 +27,57 @@
 	flagBuildMode   string
 	flagBuildDirCgo string
 	flagOutDirSwift string
+	flagProject     string
 	flagReleaseMode bool
 	flagTargetArch  string
 
 	targetArchs []string
+
+	selectedProject *project
+	projects        = []project{
+		{
+			name:                       "VanadiumCore",
+			commonHeaderPath:           "release/go/src/v.io/x/swift/types.h",
+			description:                "Core bindings from Swift to Vanadium; incompatible with SyncbaseCore",
+			directoryName:              "VanadiumCore",
+			exportedHeadersPackageRoot: "v.io/x",
+			frameworkName:              "VanadiumCore.framework",
+			frameworkBinaryName:        "VanadiumCore",
+			libraryBinaryName:          "v23",
+			mainPackage:                "v.io/x/swift/main",
+			testCheckExportedSymbols:   []string{"swift_io_v_v23_V_nativeInitGlobal", "swift_io_v_v23_context_VContext_nativeWithCancel"},
+			testCheckSharedTypes:       []string{"SwiftByteArray", "SwiftByteArrayArray", "GoContextHandle"},
+		},
+		{
+			name:                       "SyncbaseCore",
+			commonHeaderPath:           "release/go/src/v.io/x/ref/services/syncbase/bridge/cgo/lib.h",
+			description:                "Core bindings from Swift to Syncbase; incompatible with VanadiumCore",
+			directoryName:              "SyncbaseCore",
+			exportedHeadersPackageRoot: "v.io/x",
+			frameworkName:              "SyncbaseCore.framework",
+			frameworkBinaryName:        "SyncbaseCore",
+			libraryBinaryName:          "sbcore",
+			mainPackage:                "v.io/x/ref/services/syncbase/bridge/cgo",
+			testCheckExportedSymbols:   []string{"v23_syncbase_Init", "v23_syncbase_DbLeaveSyncgroup", "v23_syncbase_RowDelete"},
+			testCheckSharedTypes:       []string{"v23_syncbase_String", "v23_syncbase_Bytes", "v23_syncbase_Strings", "v23_syncbase_VError"},
+		},
+	}
 )
 
+type project struct {
+	name                       string
+	commonHeaderPath           string
+	description                string
+	directoryName              string
+	exportedHeadersPackageRoot string
+	frameworkName              string
+	frameworkBinaryName        string
+	libraryBinaryName          string
+	mainPackage                string
+	testCheckExportedSymbols   []string
+	testCheckSharedTypes       []string
+}
+
 const (
 	// darwin/386 is not a supported configuration for Go 1.5.1
 	// TODO(zinman): Support mac target instead of pure iOS
@@ -44,16 +90,13 @@
 	buildModeArchive = "c-archive"
 	buildModeShared  = "c-shared"
 
-	frameworkName       = "VanadiumCore.framework"
-	frameworkBinaryName = "VanadiumCore"
-	libraryBinaryName   = "v23"
-
 	stageBuildCgo       = "cgo"
 	stageBuildFramework = "framework"
 
 	descBuildMode   = "The build mode for cgo, either c-archive or c-shared. Defaults to c-archive."
 	descBuildDirCgo = "The directory for all generated artifacts during the cgo building phase. Defaults to a temp dir."
 	descOutDirSwift = "The directory for the generated Swift framework."
+	descProject     = "Selects which project to build (VanadiumCore, SyncbaseCore). Must be set."
 	descReleaseMode = "If set xcode is built in release mode. Defaults to false, which is debug mode."
 	descTargetArch  = "The architecture you wish to build for (arm, arm64, amd64), or 'all'. Defaults to amd64."
 )
@@ -63,6 +106,7 @@
 	cmdBuild.Flags.StringVar(&flagBuildMode, "build-mode", buildModeArchive, descBuildMode)
 	cmdBuild.Flags.StringVar(&flagBuildDirCgo, "build-dir-cgo", "", descBuildDirCgo)
 	cmdBuild.Flags.StringVar(&flagOutDirSwift, "out-dir-swift", "", descOutDirSwift)
+	cmdBuild.Flags.StringVar(&flagProject, "project", "", descProject)
 	cmdBuild.Flags.BoolVar(&flagReleaseMode, "release-mode", false, descReleaseMode)
 	cmdBuild.Flags.StringVar(&flagTargetArch, "target", targetArchAmd64, descTargetArch)
 }
@@ -77,25 +121,25 @@
 // cmdRun represents the "jiri run" command.
 var cmdRoot = &cmdline.Command{
 	Name:     "swift",
-	Short:    "Compile the Swift framework",
-	Long:     "Manages the build pipeline for the Swift framework, from CGO bindings to fattening the binaries.",
+	Short:    "Compile Swift frameworks and apps",
+	Long:     "Manages the build pipeline for the Swift framework/app, from CGO bindings to fattening the binaries.",
 	Children: []*cmdline.Command{cmdBuild, cmdClean},
 }
 
 var cmdBuild = &cmdline.Command{
 	Runner: jiri.RunnerFunc(runBuild),
 	Name:   "build",
-	Short:  "Builds and installs the cgo wrapper, as well as the Swift framework",
+	Short:  "Builds and installs the cgo wrapper, as well as the Swift framework/app",
 	Long: `The complete build pipeline from creating the CGO library, manipulating the headers for Swift,
-	and building the Swift framework using Xcode.`,
+	and building the Swift framework/app using Xcode for the selected project.`,
 	ArgsName: "[stage ...] (cgo, framework)",
 	ArgsLong: `
 	[stage ...] are the pipelines stage to run and any arguments to pass to that stage. If left empty defaults
-	to building all stages.
+	to building all stages. Project must be set.
 
 	Available stages:
 		cgo: Builds and installs the cgo library
-		framework: Builds the Swift Framework using Xcode
+		framework: Builds a Swift framework using Xcode
 	`,
 }
 
@@ -103,7 +147,21 @@
 	Runner: jiri.RunnerFunc(runClean),
 	Name:   "clean",
 	Short:  "Removes generated cgo binaries and headers",
-	Long:   "Removes generated cgo binaries and headers that fall under $JIRI_ROOT/release/swift/lib/VanadiumCore/x",
+	Long:   "Removes generated cgo binaries and headers that fall under $JIRI_ROOT/release/swift/$PROJECT/Generated",
+}
+
+func parseProjectFlag() error {
+	for _, p := range projects {
+		if strings.ToLower(flagProject) == strings.ToLower(p.name) {
+			selectedProject = &p
+			return nil
+		}
+	}
+	names := []string{}
+	for _, p := range projects {
+		names = append(names, p.name)
+	}
+	return fmt.Errorf("You must set a project -- one of the following (case-insensitive): %v", names)
 }
 
 func parseBuildFlags() error {
@@ -175,6 +233,9 @@
 }
 
 func runClean(jirix *jiri.X, args []string) error {
+	if err := parseProjectFlag(); err != nil {
+		return err
+	}
 	swiftTargetDir := getSwiftTargetDir(jirix)
 	if pathExists(swiftTargetDir) {
 		sanityCheckDir(swiftTargetDir)
@@ -188,7 +249,9 @@
 	if runtime.GOOS != "darwin" {
 		return fmt.Errorf("Only darwin is currently supported")
 	}
-
+	if err := parseProjectFlag(); err != nil {
+		return err
+	}
 	if err := parseBuildFlags(); err != nil {
 		return err
 	}
diff --git a/jiri-swift/util.go b/jiri-swift/util.go
index b53de97..7707c71 100644
--- a/jiri-swift/util.go
+++ b/jiri-swift/util.go
@@ -47,7 +47,7 @@
 }
 
 func getSwiftTargetDir(jirix *jiri.X) string {
-	return path.Join(jirix.Root, "release", "swift", "lib", frameworkBinaryName, "x")
+	return path.Join(jirix.Root, "release", "swift", selectedProject.directoryName, "Generated")
 }
 
 func findHeadersUnderPath(path string) []string {
diff --git a/jiridoc/doc.go b/jiridoc/doc.go
index 3d95d27..66c906d 100644
--- a/jiridoc/doc.go
+++ b/jiridoc/doc.go
@@ -33,7 +33,7 @@
    profile-v23  Manage v23 profiles
    run          Run an executable using the specified profile and target's
                 environment
-   swift        Compile the Swift framework
+   swift        Compile Swift frameworks and apps
    test         Manage vanadium tests
 
 The jiri additional help topics are:
@@ -1525,9 +1525,9 @@
  -v=false
    Print verbose output.
 
-Jiri swift - Compile the Swift framework
+Jiri swift - Compile Swift frameworks and apps
 
-Manages the build pipeline for the Swift framework, from CGO bindings to
+Manages the build pipeline for the Swift framework/app, from CGO bindings to
 fattening the binaries.
 
 Usage:
@@ -1535,7 +1535,7 @@
 
 The jiri swift commands are:
    build       Builds and installs the cgo wrapper, as well as the Swift
-               framework
+               framework/app
    clean       Removes generated cgo binaries and headers
 
 The jiri swift flags are:
@@ -1548,18 +1548,18 @@
 
 The complete build pipeline from creating the CGO library, manipulating the
 headers for Swift,
-	and building the Swift framework using Xcode.
+	and building the Swift framework/app using Xcode for the selected project.
 
 Usage:
    jiri swift build [flags] [stage ...] (cgo, framework)
 
 [stage ...] are the pipelines stage to run and any arguments to pass to that
 stage. If left empty defaults
-	to building all stages.
+	to building all stages. Project must be set.
 
 	Available stages:
 		cgo: Builds and installs the cgo library
-		framework: Builds the Swift Framework using Xcode
+		framework: Builds a Swift framework using Xcode
 
 The jiri swift build flags are:
  -build-dir-cgo=
@@ -1569,6 +1569,8 @@
    The build mode for cgo, either c-archive or c-shared. Defaults to c-archive.
  -out-dir-swift=
    The directory for the generated Swift framework.
+ -project=
+   Selects which project to build (VanadiumCore, SyncbaseCore). Must be set.
  -release-mode=false
    If set xcode is built in release mode. Defaults to false, which is debug
    mode.
@@ -1584,7 +1586,7 @@
 Jiri swift clean - Removes generated cgo binaries and headers
 
 Removes generated cgo binaries and headers that fall under
-$JIRI_ROOT/release/swift/lib/VanadiumCore/x
+$JIRI_ROOT/release/swift/$PROJECT/Generated
 
 Usage:
    jiri swift clean [flags]