v.io/jiri: switch to v4 profile manifests

- add cleanup --rewrite-profile-manifest which will update the manifest file
- switch default versions to v4.

MultiPart: 1/2

Change-Id: Ifd61c5461a07370eac23845ee57846c81dbe1234
diff --git a/profiles/commandline/driver.go b/profiles/commandline/driver.go
index 94e9df9..6fa89ef 100644
--- a/profiles/commandline/driver.go
+++ b/profiles/commandline/driver.go
@@ -126,6 +126,7 @@
 	specificVersionsFlag bool
 	cleanupFlag          bool
 	rmAllFlag            bool
+	rewriteManifestFlag  bool
 )
 
 func Main(name string) {
@@ -183,8 +184,8 @@
 	// uninstall accept --all-targets but with different defaults.
 	cmdUninstall.Flags.BoolVar(&allFlag, "all-targets", false, "apply to all targets for the specified profile(s)")
 
-	// list accepts --show-manifest, --available, --dir, --default, --versions
-	cmdList.Flags.BoolVar(&showManifestFlag, "show-manifest", false, "print out the manifest file")
+	// list accepts --show-profiles-manifest, --available, --dir, --default, --versions
+	cmdList.Flags.BoolVar(&showManifestFlag, "show-profiles-manifest", false, "print out the manifest file")
 	cmdList.Flags.BoolVar(&availableFlag, "available", false, "print the list of available profiles")
 	cmdList.Flags.StringVar(&infoFlag, "info", "", infoUsage())
 
@@ -196,7 +197,8 @@
 	// cleanup accepts the following flags:
 	cmdCleanup.Flags.BoolVar(&cleanupFlag, "gc", false, "uninstall profile targets that are older than the current default")
 	cmdCleanup.Flags.BoolVar(&specificVersionsFlag, "ensure-specific-versions-are-set", false, "ensure that profile targets have a specific version set")
-	cmdCleanup.Flags.BoolVar(&rmAllFlag, "rm-all", false, "remove profile manifest and all profile generated output files.")
+	cmdCleanup.Flags.BoolVar(&rmAllFlag, "rm-all", false, "remove profiles manifest and all profile generated output files.")
+	cmdCleanup.Flags.BoolVar(&rewriteManifestFlag, "rewrite-profiles-manifest", false, "rewrite the profiles manifest file to use the latest schema version")
 }
 
 func runList(env *cmdline.Env, args []string) error {
@@ -592,6 +594,9 @@
 		// Don't write out the profiles manifest file again.
 		return nil
 	}
+	if rewriteManifestFlag {
+		dirty = true
+	}
 	if !dirty {
 		return fmt.Errorf("at least one option must be specified")
 	}
diff --git a/profiles/manifest.go b/profiles/manifest.go
index 4631ac6..cb1c505 100644
--- a/profiles/manifest.go
+++ b/profiles/manifest.go
@@ -83,7 +83,7 @@
 }
 
 func newDB() *profileDB {
-	return &profileDB{db: make(map[string]*Profile), version: 0}
+	return &profileDB{db: make(map[string]*Profile), version: V4}
 }
 
 var (
@@ -283,7 +283,7 @@
 	defer pdb.Unlock()
 
 	var schema profilesSchema
-	schema.Version = V3
+	schema.Version = V4
 	for i, name := range pdb.profilesUnlocked() {
 		profile := pdb.db[name]
 		schema.Profiles = append(schema.Profiles, &profileSchema{
diff --git a/profiles/manifest_test.go b/profiles/manifest_test.go
index 8a8d2dd..aa6ef33 100644
--- a/profiles/manifest_test.go
+++ b/profiles/manifest_test.go
@@ -16,6 +16,7 @@
 	"testing"
 
 	"v.io/jiri/profiles"
+	"v.io/jiri/project"
 	"v.io/jiri/tool"
 )
 
@@ -121,7 +122,7 @@
 	}
 }
 
-func TestBackwardsCompatibility(t *testing.T) {
+func TestReadingV0(t *testing.T) {
 	profiles.Clear()
 
 	getProfiles := func() []*profiles.Profile {
@@ -162,7 +163,7 @@
 		t.Fatal(err)
 	}
 
-	if got, want := profiles.SchemaVersion(), profiles.V3; got != want {
+	if got, want := profiles.SchemaVersion(), profiles.V4; got != want {
 		t.Errorf("got %v, want %v", got, want)
 	}
 	nprofiles := getProfiles()
@@ -180,3 +181,54 @@
 		}
 	}
 }
+
+func handleRelativePath(root profiles.RelativePath, s string) string {
+	// Handle the transition from absolute to relative paths.
+	if filepath.IsAbs(s) {
+		return s
+	}
+	return root.RootJoin(s).Expand()
+}
+
+func TestReadingV3AndV4(t *testing.T) {
+	ctx := tool.NewDefaultContext()
+	root, err := project.JiriRoot()
+	if err != nil {
+		t.Fatal(err)
+	}
+	for i, c := range []struct {
+		filename, prefix, variable string
+		version                    profiles.Version
+	}{
+		{"v3.xml", "", "", profiles.V3},
+		{"v4.xml", root, "${JIRI_ROOT}", profiles.V4},
+	} {
+		ch, err := profiles.NewConfigHelper(ctx, profiles.UseProfiles, filepath.Join("testdata", c.filename))
+		if err != nil {
+			t.Fatal(err)
+		}
+		if got, want := profiles.SchemaVersion(), c.version; got != want {
+			t.Errorf("%d: got %v, want %v", i, got, want)
+		}
+		target, err := profiles.NewTarget("cpu1-os1@1")
+		if err != nil {
+			t.Fatal(err)
+		}
+		p := profiles.LookupProfile("a")
+		// We need to expand the variable here for a V4 profile if we want
+		// to get the full absolute path.
+		if got, want := p.Root, c.variable+"/an/absolute/root"; got != want {
+			t.Errorf("%d: got %v, want %v", i, got, want)
+		}
+		lt := profiles.LookupProfileTarget("a", target)
+		if got, want := lt.InstallationDir, c.variable+"/an/absolute/dir"; got != want {
+			t.Errorf("%d: got %v, want %v", i, got, want)
+		}
+		// The merged environment variables are expanded appropriately
+		// internally by MergeEnvFromProfiles.
+		ch.MergeEnvFromProfiles(profiles.JiriMergePolicies(), target, "a")
+		if got, want := ch.Get("ABS"), "-I"+c.prefix+"/an/absolute/path"; got != want {
+			t.Errorf("%d: got %v, want %v", i, got, want)
+		}
+	}
+}
diff --git a/profiles/testdata/m1.xml b/profiles/testdata/m1.xml
index d54ea6f..c14d319 100644
--- a/profiles/testdata/m1.xml
+++ b/profiles/testdata/m1.xml
@@ -1,4 +1,4 @@
-<profiles version="3">
+<profiles version="4">
   <profile name="a" root="">
     <target tag="" arch="cpu1" os="os1" installation-directory="" version="1">
       <envvars></envvars>
diff --git a/profiles/testdata/v3.xml b/profiles/testdata/v3.xml
new file mode 100644
index 0000000..5b6a3cb
--- /dev/null
+++ b/profiles/testdata/v3.xml
@@ -0,0 +1,13 @@
+<profiles version="3">
+  <profile name="a" root="/an/absolute/root">
+    <target arch="cpu1" os="os1" installation-directory="/an/absolute/dir" version="1">
+      <envvars>
+        <var>ABS=-I/an/absolute/path</var>
+      </envvars>
+      <command-line>
+        <var>A=B</var>
+        <var>C=D</var>
+      </command-line>
+    </target>
+  </profile>
+</profiles>
diff --git a/profiles/testdata/v4.xml b/profiles/testdata/v4.xml
new file mode 100644
index 0000000..83fddb7
--- /dev/null
+++ b/profiles/testdata/v4.xml
@@ -0,0 +1,13 @@
+<profiles version="4">
+  <profile name="a" root="${JIRI_ROOT}/an/absolute/root">
+    <target arch="cpu1" os="os1" installation-directory="${JIRI_ROOT}/an/absolute/dir" version="1">
+      <envvars>
+        <var>ABS=-I${JIRI_ROOT}/an/absolute/path</var>
+      </envvars>
+      <command-line>
+        <var>A=B</var>
+        <var>C=D</var>
+      </command-line>
+    </target>
+  </profile>
+</profiles>