v.io/jiri: split out installation of os packages

This CL adds the os-packages subcommand to jiri profile.
The os-packages command is used to list and optionally
install OS packages required by the specified profiles.
This avoids the need to run all of the jiri profile
install commands as root, rather only the output
of jiri profile os-packages needs to be run as root.
Eg:

sudo $(jiri profile os-packages)

or:

sudo jiri profile os-packages --install-packages

MultiPart: 2/2
Change-Id: Ibd9608d39107a3baa7d389dfe78d2c181503e0d1
diff --git a/jiri-profile-v23/android_profile/.api b/jiri-profile-v23/android_profile/.api
index 1d0b322..43bf001 100644
--- a/jiri-profile-v23/android_profile/.api
+++ b/jiri-profile-v23/android_profile/.api
@@ -1,6 +1,7 @@
 pkg android_profile, func Register(string, string)
 pkg android_profile, method (*Manager) AddFlags(*flag.FlagSet, profiles.Action)
 pkg android_profile, method (*Manager) Install(*jiri.X, *profiles.DB, jiri.RelPath, profiles.Target) error
+pkg android_profile, method (*Manager) OSPackages(*jiri.X, *profiles.DB, jiri.RelPath, profiles.Target) ([]string, error)
 pkg android_profile, method (*Manager) Uninstall(*jiri.X, *profiles.DB, jiri.RelPath, profiles.Target) error
 pkg android_profile, method (Manager) Info() string
 pkg android_profile, method (Manager) Installer() string
diff --git a/jiri-profile-v23/android_profile/android.go b/jiri-profile-v23/android_profile/android.go
index 80e514b..631311e 100644
--- a/jiri-profile-v23/android_profile/android.go
+++ b/jiri-profile-v23/android_profile/android.go
@@ -180,6 +180,19 @@
 	return nil
 }
 
+func (m *Manager) OSPackages(jirix *jiri.X, pdb *profiles.DB, root jiri.RelPath, target profiles.Target) ([]string, error) {
+	var pkgs []string
+	switch runtime.GOOS {
+	case "linux":
+		pkgs = []string{"ant", "autoconf", "bzip2", "default-jdk", "gawk", "lib32z1", "lib32stdc++6"}
+	case "darwin":
+		pkgs = []string{"ant", "autoconf", "gawk"}
+	default:
+		return nil, fmt.Errorf("unsupported OS: %s", runtime.GOOS)
+	}
+	return pkgs, nil
+}
+
 func (m *Manager) Install(jirix *jiri.X, pdb *profiles.DB, root jiri.RelPath, target profiles.Target) error {
 	var err error
 	var baseEnv []string
@@ -241,19 +254,6 @@
 
 // installAndroidNDK installs the android NDK toolchain.
 func (m *Manager) installAndroidNDK(jirix *jiri.X, target profiles.Target) (e error) {
-	// Install dependencies.
-	var pkgs []string
-	switch runtime.GOOS {
-	case "linux":
-		pkgs = []string{"ant", "autoconf", "bzip2", "default-jdk", "gawk", "lib32z1", "lib32stdc++6"}
-	case "darwin":
-		pkgs = []string{"ant", "autoconf", "gawk"}
-	default:
-		return fmt.Errorf("unsupported OS: %s", runtime.GOOS)
-	}
-	if err := profilesutil.InstallPackages(jirix, pkgs); err != nil {
-		return err
-	}
 	// Download Android NDK.
 	installNdkFn := func() error {
 		s := jirix.NewSeq()
diff --git a/jiri-profile-v23/base_profile/.api b/jiri-profile-v23/base_profile/.api
index ec295b8..e581fed 100644
--- a/jiri-profile-v23/base_profile/.api
+++ b/jiri-profile-v23/base_profile/.api
@@ -1,6 +1,7 @@
 pkg base_profile, func Register(string, string)
 pkg base_profile, method (*Manager) AddFlags(*flag.FlagSet, profiles.Action)
 pkg base_profile, method (*Manager) Install(*jiri.X, *profiles.DB, jiri.RelPath, profiles.Target) error
+pkg base_profile, method (*Manager) OSPackages(*jiri.X, *profiles.DB, jiri.RelPath, profiles.Target) ([]string, error)
 pkg base_profile, method (*Manager) Uninstall(*jiri.X, *profiles.DB, jiri.RelPath, profiles.Target) error
 pkg base_profile, method (Manager) Info() string
 pkg base_profile, method (Manager) Installer() string
diff --git a/jiri-profile-v23/base_profile/base.go b/jiri-profile-v23/base_profile/base.go
index 71d7c0a..0c3de5c 100644
--- a/jiri-profile-v23/base_profile/base.go
+++ b/jiri-profile-v23/base_profile/base.go
@@ -12,7 +12,6 @@
 	"v.io/jiri/profiles"
 	"v.io/jiri/profiles/profilesmanager"
 	"v.io/jiri/profiles/profilesreader"
-	"v.io/jiri/profiles/profilesutil"
 	"v.io/x/lib/envvar"
 )
 
@@ -83,14 +82,15 @@
 func (m *Manager) AddFlags(flags *flag.FlagSet, action profiles.Action) {
 }
 
-func (m *Manager) Install(jirix *jiri.X, pdb *profiles.DB, root jiri.RelPath, target profiles.Target) error {
-	// Install packages
+func (m *Manager) OSPackages(jirix *jiri.X, pdb *profiles.DB, root jiri.RelPath, target profiles.Target) ([]string, error) {
 	if !target.CrossCompiling() && target.OS() == "linux" && (target.Version() == "1" || target.Version() == "2" || target.Version() == "3" || target.Version() == "4") {
 		// Version 5 onwards uses go 1.6+, where there is no need for "libssl-dev".
-		if err := profilesutil.InstallPackages(jirix, []string{"libssl-dev"}); err != nil {
-			return err
-		}
+		return []string{"libssl-dev"}, nil
 	}
+	return nil, nil
+}
+
+func (m *Manager) Install(jirix *jiri.X, pdb *profiles.DB, root jiri.RelPath, target profiles.Target) error {
 	if err := m.versionInfo.Lookup(target.Version(), &m.spec); err != nil {
 		return err
 	}
diff --git a/jiri-profile-v23/dart_profile/.api b/jiri-profile-v23/dart_profile/.api
index a40a9d2..ad10044 100644
--- a/jiri-profile-v23/dart_profile/.api
+++ b/jiri-profile-v23/dart_profile/.api
@@ -1,6 +1,7 @@
 pkg dart_profile, func Register(string, string)
 pkg dart_profile, method (*Manager) AddFlags(*flag.FlagSet, profiles.Action)
 pkg dart_profile, method (*Manager) Install(*jiri.X, *profiles.DB, jiri.RelPath, profiles.Target) error
+pkg dart_profile, method (*Manager) OSPackages(*jiri.X, *profiles.DB, jiri.RelPath, profiles.Target) ([]string, error)
 pkg dart_profile, method (*Manager) Uninstall(*jiri.X, *profiles.DB, jiri.RelPath, profiles.Target) error
 pkg dart_profile, method (Manager) Info() string
 pkg dart_profile, method (Manager) Installer() string
diff --git a/jiri-profile-v23/dart_profile/dart.go b/jiri-profile-v23/dart_profile/dart.go
index 874612a..b3cc8c5 100644
--- a/jiri-profile-v23/dart_profile/dart.go
+++ b/jiri-profile-v23/dart_profile/dart.go
@@ -80,6 +80,10 @@
 	return nil
 }
 
+func (m *Manager) OSPackages(jirix *jiri.X, pdb *profiles.DB, root jiri.RelPath, target profiles.Target) ([]string, error) {
+	return nil, nil
+}
+
 func (m *Manager) Install(jirix *jiri.X, pdb *profiles.DB, root jiri.RelPath, target profiles.Target) error {
 	if err := m.initForTarget(jirix, root, &target); err != nil {
 		return err
diff --git a/jiri-profile-v23/doc.go b/jiri-profile-v23/doc.go
index 500261e..f039925 100644
--- a/jiri-profile-v23/doc.go
+++ b/jiri-profile-v23/doc.go
@@ -79,6 +79,8 @@
 
 The jiri profile-v23 commands are:
    install     Install the given profiles
+   os-packages List the commands to install the OS packages required by the
+               given profiles
    uninstall   Uninstall the given profiles
    update      Install the latest default version of the given profiles
    cleanup     Cleanup the locally installed profiles
@@ -129,6 +131,42 @@
  -v=false
    Print verbose output.
 
+Jiri profile-v23 os-packages - List the commands to install the OS packages required by the given profiles
+
+List or optionally run the commands to install the OS packages required by the
+given profiles.
+
+Usage:
+   jiri profile-v23 os-packages [flags] <profiles>
+
+<profiles> is a list of profiles to list OS packages for.
+
+The jiri profile-v23 os-packages flags are:
+ -all=false
+   print commands to install all required OS packages, not just those that are
+   missing
+ -env=
+   specify an environment variable in the form: <var>=[<val>],...
+ -go.sysroot-image=
+   sysroot image for cross compiling to the currently specified target
+ -go.sysroot-image-dirs-to-use=/lib:/usr/lib:/usr/include
+   a colon separated list of directories to use from the sysroot image
+ -install=false
+   install the requested packages. This may need to be run as root.
+ -mojodev.dir=
+   Path of mojo repo checkout.
+ -profiles-db=$JIRI_ROOT/.jiri_root/profile_db
+   the path, relative to JIRI_ROOT, that contains the profiles database.
+ -profiles-dir=.jiri_root/profiles
+   the directory, relative to JIRI_ROOT, that profiles are installed in
+ -target=<runtime.GOARCH>-<runtime.GOOS>
+   specifies a profile target in the following form: <arch>-<os>[@<version>]
+
+ -color=true
+   Use color to format output.
+ -v=false
+   Print verbose output.
+
 Jiri profile-v23 uninstall - Uninstall the given profiles
 
 Uninstall the given profiles.
diff --git a/jiri-profile-v23/go_profile/.api b/jiri-profile-v23/go_profile/.api
index 1ddc481..9710094 100644
--- a/jiri-profile-v23/go_profile/.api
+++ b/jiri-profile-v23/go_profile/.api
@@ -1,6 +1,7 @@
 pkg go_profile, func Register(string, string)
 pkg go_profile, method (*Manager) AddFlags(*flag.FlagSet, profiles.Action)
 pkg go_profile, method (*Manager) Install(*jiri.X, *profiles.DB, jiri.RelPath, profiles.Target) error
+pkg go_profile, method (*Manager) OSPackages(*jiri.X, *profiles.DB, jiri.RelPath, profiles.Target) ([]string, error)
 pkg go_profile, method (*Manager) Uninstall(*jiri.X, *profiles.DB, jiri.RelPath, profiles.Target) error
 pkg go_profile, method (Manager) Info() string
 pkg go_profile, method (Manager) Installer() string
diff --git a/jiri-profile-v23/go_profile/go.go b/jiri-profile-v23/go_profile/go.go
index 6f2bc8e..3d70135 100644
--- a/jiri-profile-v23/go_profile/go.go
+++ b/jiri-profile-v23/go_profile/go.go
@@ -59,6 +59,17 @@
 	},
 }
 
+var osPackages = map[xspec]map[xspec][]string{
+	xspec{"amd64", "darwin"}: {},
+	xspec{"amd64", "linux"}: {
+		xspec{"arm", "linux"}: []string{
+			"automake", "bison", "bzip2", "curl", "flex", "g++",
+			"gawk", "libexpat1-dev", "gettext", "gperf",
+			"libncurses5-dev", "libtool", "subversion", "texinfo",
+		},
+	},
+}
+
 type versionSpec struct {
 	gitRevision string
 	patchFiles  []string
@@ -94,6 +105,13 @@
 	return &goRelease{file, sha256}
 }
 
+func (m *Manager) OSPackages(jirix *jiri.X, pdb *profiles.DB, root jiri.RelPath, target profiles.Target) ([]string, error) {
+	t := osPackages[xspec{"foo", "bar"}][xspec{"baz", "boo"}]
+	t = osPackages[xspec{runtime.GOARCH, runtime.GOOS}][xspec{"baz", "boo"}]
+	_ = t
+	return osPackages[xspec{runtime.GOARCH, runtime.GOOS}][xspec{target.Arch(), target.OS()}], nil
+}
+
 func (g *goRelease) install(jirix *jiri.X, dir string) error {
 	s := jirix.NewSeq()
 	tmpDir, err := s.TempDir("", "")
@@ -429,14 +447,6 @@
 		}
 		return "", nil, nil
 	}
-	// Install dependencies.
-	pkgs := []string{
-		"automake", "bison", "bzip2", "curl", "flex", "g++", "gawk", "libexpat1-dev",
-		"gettext", "gperf", "libncurses5-dev", "libtool", "subversion", "texinfo",
-	}
-	if err := profilesutil.InstallPackages(jirix, pkgs); err != nil {
-		return "", nil, err
-	}
 
 	// Build and install crosstool-ng.
 	installNgFn := func() error {
diff --git a/jiri-profile-v23/java_profile/.api b/jiri-profile-v23/java_profile/.api
index 621dff1..76b7406 100644
--- a/jiri-profile-v23/java_profile/.api
+++ b/jiri-profile-v23/java_profile/.api
@@ -1,6 +1,7 @@
 pkg java_profile, func Register(string, string)
 pkg java_profile, method (*Manager) AddFlags(*flag.FlagSet, profiles.Action)
 pkg java_profile, method (*Manager) Install(*jiri.X, *profiles.DB, jiri.RelPath, profiles.Target) error
+pkg java_profile, method (*Manager) OSPackages(*jiri.X, *profiles.DB, jiri.RelPath, profiles.Target) ([]string, error)
 pkg java_profile, method (*Manager) Uninstall(*jiri.X, *profiles.DB, jiri.RelPath, profiles.Target) error
 pkg java_profile, method (Manager) Info() string
 pkg java_profile, method (Manager) Installer() string
diff --git a/jiri-profile-v23/java_profile/java.go b/jiri-profile-v23/java_profile/java.go
index c0511c3..bee09db 100644
--- a/jiri-profile-v23/java_profile/java.go
+++ b/jiri-profile-v23/java_profile/java.go
@@ -18,7 +18,6 @@
 	"v.io/jiri/profiles"
 	"v.io/jiri/profiles/profilesmanager"
 	"v.io/jiri/profiles/profilesreader"
-	"v.io/jiri/profiles/profilesutil"
 	"v.io/x/lib/envvar"
 )
 
@@ -83,6 +82,15 @@
 	return nil
 }
 
+func (m *Manager) OSPackages(jirix *jiri.X, pdb *profiles.DB, root jiri.RelPath, target profiles.Target) ([]string, error) {
+	switch target.OS() {
+	case "darwin", "linux":
+		return []string{"gradle"}, nil
+	default:
+		return nil, fmt.Errorf("OS %q is not supported", target.OS())
+	}
+}
+
 func (m *Manager) Install(jirix *jiri.X, pdb *profiles.DB, root jiri.RelPath, target profiles.Target) error {
 	if err := m.initForTarget(root, target); err != nil {
 		return err
@@ -146,9 +154,6 @@
 func (m *Manager) install(jirix *jiri.X, target profiles.Target) (string, error) {
 	switch target.OS() {
 	case "darwin":
-		if err := profilesutil.InstallPackages(jirix, []string{"gradle"}); err != nil {
-			return "", err
-		}
 		javaHome, err := getJDKDarwin(jirix, m.spec)
 		if err == nil {
 			return javaHome, nil
@@ -161,9 +166,6 @@
 		jirix.NewSeq().Last(javaHomeBin, "-t", "CommandLine", "--request")
 		return "", fmt.Errorf("Please follow the OS X prompt instructions to install JDK, then set JAVA_HOME and re-run the profile installation command.")
 	case "linux":
-		if err := profilesutil.InstallPackages(jirix, []string{"gradle"}); err != nil {
-			return "", err
-		}
 		javaHome, err := getJDKLinux(jirix, m.spec)
 		if err == nil {
 			return javaHome, nil
diff --git a/jiri-profile-v23/mojo_dev_profile/.api b/jiri-profile-v23/mojo_dev_profile/.api
index 38fcad8..fcb7a0c 100644
--- a/jiri-profile-v23/mojo_dev_profile/.api
+++ b/jiri-profile-v23/mojo_dev_profile/.api
@@ -1,6 +1,7 @@
 pkg mojo_dev_profile, func Register(string, string)
 pkg mojo_dev_profile, method (*Manager) AddFlags(*flag.FlagSet, profiles.Action)
 pkg mojo_dev_profile, method (*Manager) Install(*jiri.X, *profiles.DB, jiri.RelPath, profiles.Target) error
+pkg mojo_dev_profile, method (*Manager) OSPackages(*jiri.X, *profiles.DB, jiri.RelPath, profiles.Target) ([]string, error)
 pkg mojo_dev_profile, method (*Manager) Uninstall(*jiri.X, *profiles.DB, jiri.RelPath, profiles.Target) error
 pkg mojo_dev_profile, method (Manager) Info() string
 pkg mojo_dev_profile, method (Manager) Installer() string
diff --git a/jiri-profile-v23/mojo_dev_profile/mojo-dev.go b/jiri-profile-v23/mojo_dev_profile/mojo-dev.go
index 47c5635..128afef 100644
--- a/jiri-profile-v23/mojo_dev_profile/mojo-dev.go
+++ b/jiri-profile-v23/mojo_dev_profile/mojo-dev.go
@@ -67,6 +67,10 @@
 	}
 }
 
+func (m *Manager) OSPackages(jirix *jiri.X, pdb *profiles.DB, root jiri.RelPath, target profiles.Target) ([]string, error) {
+	return nil, nil
+}
+
 func (m *Manager) Install(jirix *jiri.X, pdb *profiles.DB, root jiri.RelPath, target profiles.Target) error {
 	if mojoDir == "" {
 		return fmt.Errorf("flag %q must be set", mojoDirFlagName)
diff --git a/jiri-profile-v23/mojo_profile/.api b/jiri-profile-v23/mojo_profile/.api
index 43e0fbd..b828e51 100644
--- a/jiri-profile-v23/mojo_profile/.api
+++ b/jiri-profile-v23/mojo_profile/.api
@@ -1,6 +1,7 @@
 pkg mojo_profile, func Register(string, string)
 pkg mojo_profile, method (*Manager) AddFlags(*flag.FlagSet, profiles.Action)
 pkg mojo_profile, method (*Manager) Install(*jiri.X, *profiles.DB, jiri.RelPath, profiles.Target) error
+pkg mojo_profile, method (*Manager) OSPackages(*jiri.X, *profiles.DB, jiri.RelPath, profiles.Target) ([]string, error)
 pkg mojo_profile, method (*Manager) Uninstall(*jiri.X, *profiles.DB, jiri.RelPath, profiles.Target) error
 pkg mojo_profile, method (Manager) Info() string
 pkg mojo_profile, method (Manager) Installer() string
diff --git a/jiri-profile-v23/mojo_profile/mojo.go b/jiri-profile-v23/mojo_profile/mojo.go
index e7db89d..5358b06 100644
--- a/jiri-profile-v23/mojo_profile/mojo.go
+++ b/jiri-profile-v23/mojo_profile/mojo.go
@@ -446,6 +446,10 @@
 	return nil
 }
 
+func (m *Manager) OSPackages(jirix *jiri.X, pdb *profiles.DB, root jiri.RelPath, target profiles.Target) ([]string, error) {
+	return nil, nil
+}
+
 func (m *Manager) Install(jirix *jiri.X, pdb *profiles.DB, root jiri.RelPath, target profiles.Target) error {
 	if err := m.initForTarget(jirix, root, &target); err != nil {
 		return err
diff --git a/jiri-profile-v23/nacl_profile/.api b/jiri-profile-v23/nacl_profile/.api
index be9f484..5fbe338 100644
--- a/jiri-profile-v23/nacl_profile/.api
+++ b/jiri-profile-v23/nacl_profile/.api
@@ -1,6 +1,7 @@
 pkg nacl_profile, func Register(string, string)
 pkg nacl_profile, method (*Manager) AddFlags(*flag.FlagSet, profiles.Action)
 pkg nacl_profile, method (*Manager) Install(*jiri.X, *profiles.DB, jiri.RelPath, profiles.Target) error
+pkg nacl_profile, method (*Manager) OSPackages(*jiri.X, *profiles.DB, jiri.RelPath, profiles.Target) ([]string, error)
 pkg nacl_profile, method (*Manager) Uninstall(*jiri.X, *profiles.DB, jiri.RelPath, profiles.Target) error
 pkg nacl_profile, method (Manager) Info() string
 pkg nacl_profile, method (Manager) Installer() string
diff --git a/jiri-profile-v23/nacl_profile/nacl.go b/jiri-profile-v23/nacl_profile/nacl.go
index 1112c9a..420910a 100644
--- a/jiri-profile-v23/nacl_profile/nacl.go
+++ b/jiri-profile-v23/nacl_profile/nacl.go
@@ -93,6 +93,17 @@
 	return nil
 }
 
+func (m *Manager) OSPackages(jirix *jiri.X, pdb *profiles.DB, root jiri.RelPath, target profiles.Target) ([]string, error) {
+	switch runtime.GOOS {
+	case "darwin":
+	case "linux":
+		return []string{"g++", "libc6-i386", "zip"}, nil
+	default:
+		return nil, fmt.Errorf("%q is not supported", target.OS)
+	}
+	return nil, nil
+}
+
 func (m *Manager) Install(jirix *jiri.X, pdb *profiles.DB, root jiri.RelPath, target profiles.Target) error {
 	if err := m.initForTarget(jirix, "installed", root, &target); err != nil {
 		return err
@@ -131,13 +142,6 @@
 
 // installNacl installs the nacl profile.
 func (m *Manager) installNacl(jirix *jiri.X, target profiles.Target, spec versionSpec) error {
-	switch runtime.GOOS {
-	case "darwin":
-	case "linux":
-		if err := profilesutil.InstallPackages(jirix, []string{"g++", "libc6-i386", "zip"}); err != nil {
-			return err
-		}
-	}
 	naclSrcDir := m.naclSrcDir.Abs(jirix)
 	naclInstDir := m.naclInstDir.Abs(jirix)
 	cloneGoPpapiFn := func() error {
diff --git a/jiri-profile-v23/nodejs_profile/.api b/jiri-profile-v23/nodejs_profile/.api
index e98ab83..712779a 100644
--- a/jiri-profile-v23/nodejs_profile/.api
+++ b/jiri-profile-v23/nodejs_profile/.api
@@ -1,6 +1,7 @@
 pkg nodejs_profile, func Register(string, string)
 pkg nodejs_profile, method (*Manager) AddFlags(*flag.FlagSet, profiles.Action)
 pkg nodejs_profile, method (*Manager) Install(*jiri.X, *profiles.DB, jiri.RelPath, profiles.Target) error
+pkg nodejs_profile, method (*Manager) OSPackages(*jiri.X, *profiles.DB, jiri.RelPath, profiles.Target) ([]string, error)
 pkg nodejs_profile, method (*Manager) Uninstall(*jiri.X, *profiles.DB, jiri.RelPath, profiles.Target) error
 pkg nodejs_profile, method (Manager) Info() string
 pkg nodejs_profile, method (Manager) Installer() string
diff --git a/jiri-profile-v23/nodejs_profile/nodejs.go b/jiri-profile-v23/nodejs_profile/nodejs.go
index bdf5ee5..bcf8b8c 100644
--- a/jiri-profile-v23/nodejs_profile/nodejs.go
+++ b/jiri-profile-v23/nodejs_profile/nodejs.go
@@ -75,6 +75,19 @@
 	return nil
 }
 
+func (m *Manager) OSPackages(jirix *jiri.X, pdb *profiles.DB, root jiri.RelPath, target profiles.Target) ([]string, error) {
+	switch target.OS() {
+	case "darwin":
+	case "linux":
+		if target.Version() == "0.10.24" {
+			return []string{"g++"}, nil
+		}
+	default:
+		return nil, fmt.Errorf("%q is not supported", target.OS)
+	}
+	return nil, nil
+}
+
 func (m *Manager) Install(jirix *jiri.X, pdb *profiles.DB, root jiri.RelPath, target profiles.Target) error {
 	if err := m.initForTarget(root, target); err != nil {
 		return err
@@ -166,15 +179,6 @@
 }
 
 func (m *Manager) installNodeFromSource(jirix *jiri.X, target profiles.Target) error {
-	switch target.OS() {
-	case "darwin":
-	case "linux":
-		if err := profilesutil.InstallPackages(jirix, []string{"g++"}); err != nil {
-			return err
-		}
-	default:
-		return fmt.Errorf("%q is not supported", target.OS)
-	}
 	// Build and install NodeJS.
 	installNodeFn := func() error {
 		return jirix.NewSeq().Pushd(m.nodeSrcDir.Abs(jirix)).
diff --git a/jiri-profile-v23/syncbase_profile/.api b/jiri-profile-v23/syncbase_profile/.api
index d5b54c0..6edbbf0 100644
--- a/jiri-profile-v23/syncbase_profile/.api
+++ b/jiri-profile-v23/syncbase_profile/.api
@@ -1,6 +1,7 @@
 pkg syncbase_profile, func Register(string, string)
 pkg syncbase_profile, method (*Manager) AddFlags(*flag.FlagSet, profiles.Action)
 pkg syncbase_profile, method (*Manager) Install(*jiri.X, *profiles.DB, jiri.RelPath, profiles.Target) error
+pkg syncbase_profile, method (*Manager) OSPackages(*jiri.X, *profiles.DB, jiri.RelPath, profiles.Target) ([]string, error)
 pkg syncbase_profile, method (*Manager) Uninstall(*jiri.X, *profiles.DB, jiri.RelPath, profiles.Target) error
 pkg syncbase_profile, method (Manager) Info() string
 pkg syncbase_profile, method (Manager) Installer() string
diff --git a/jiri-profile-v23/syncbase_profile/syncbase.go b/jiri-profile-v23/syncbase_profile/syncbase.go
index 921a673..4abd1c3 100644
--- a/jiri-profile-v23/syncbase_profile/syncbase.go
+++ b/jiri-profile-v23/syncbase_profile/syncbase.go
@@ -122,11 +122,21 @@
 	return env.ToSlice(), nil
 }
 
+func (m *Manager) OSPackages(jirix *jiri.X, pdb *profiles.DB, root jiri.RelPath, target profiles.Target) ([]string, error) {
+	switch runtime.GOOS {
+	case "darwin":
+		return []string{"autoconf", "automake", "libtool", "pkg-config"}, nil
+	case "linux":
+		return []string{"autoconf", "automake", "g++", "g++-multilib",
+			"gcc-multilib", "libtool", "pkg-config"}, nil
+	default:
+		return nil, fmt.Errorf("%q is not supported", runtime.GOOS)
+	}
+	return nil, nil
+}
+
 func (m *Manager) Install(jirix *jiri.X, pdb *profiles.DB, root jiri.RelPath, target profiles.Target) error {
 	m.initForTarget(jirix, root, target)
-	if err := m.installDependencies(jirix, target.Arch(), target.OS()); err != nil {
-		return err
-	}
 	if err := m.installCommon(jirix, pdb, root, target); err != nil {
 		return err
 	}
@@ -153,23 +163,6 @@
 	return nil
 }
 
-func (m *Manager) installDependencies(jirix *jiri.X, arch, OS string) error {
-	var pkgs []string
-	switch runtime.GOOS {
-	case "darwin":
-		pkgs = []string{
-			"autoconf", "automake", "libtool", "pkg-config",
-		}
-	case "linux":
-		pkgs = []string{
-			"autoconf", "automake", "g++", "g++-multilib", "gcc-multilib", "libtool", "pkg-config",
-		}
-	default:
-		return fmt.Errorf("%q is not supported", runtime.GOOS)
-	}
-	return profilesutil.InstallPackages(jirix, pkgs)
-}
-
 // initXCC sets the environment variables in 'env' for use with cross-compilers.
 func (m *Manager) initXCC(env map[string]string, pdb *profiles.DB, target profiles.Target) error {
 	target.SetVersion("")
diff --git a/jiri-profile-v23/terraform_profile/.api b/jiri-profile-v23/terraform_profile/.api
index beb5e19..acda2f3 100644
--- a/jiri-profile-v23/terraform_profile/.api
+++ b/jiri-profile-v23/terraform_profile/.api
@@ -1,6 +1,7 @@
 pkg terraform_profile, func Register(string, string)
 pkg terraform_profile, method (*Manager) AddFlags(*flag.FlagSet, profiles.Action)
 pkg terraform_profile, method (*Manager) Install(*jiri.X, *profiles.DB, jiri.RelPath, profiles.Target) error
+pkg terraform_profile, method (*Manager) OSPackages(*jiri.X, *profiles.DB, jiri.RelPath, profiles.Target) ([]string, error)
 pkg terraform_profile, method (*Manager) Uninstall(*jiri.X, *profiles.DB, jiri.RelPath, profiles.Target) error
 pkg terraform_profile, method (Manager) Info() string
 pkg terraform_profile, method (Manager) Installer() string
diff --git a/jiri-profile-v23/terraform_profile/terraform.go b/jiri-profile-v23/terraform_profile/terraform.go
index bc60a54..1353d8f 100644
--- a/jiri-profile-v23/terraform_profile/terraform.go
+++ b/jiri-profile-v23/terraform_profile/terraform.go
@@ -66,6 +66,10 @@
 	return nil
 }
 
+func (m *Manager) OSPackages(jirix *jiri.X, pdb *profiles.DB, root jiri.RelPath, target profiles.Target) ([]string, error) {
+	return nil, nil
+}
+
 func (m *Manager) Install(jirix *jiri.X, pdb *profiles.DB, root jiri.RelPath, target profiles.Target) error {
 	if err := m.initForTarget(root, target); err != nil {
 		return err
diff --git a/jiridoc/doc.go b/jiridoc/doc.go
index d8727da..2b056a3 100644
--- a/jiridoc/doc.go
+++ b/jiridoc/doc.go
@@ -252,6 +252,8 @@
    list        List available or installed profiles
    env         Display profile environment variables
    install     Install the given profiles
+   os-packages List the commands to install the OS packages required by the
+               given profiles
    uninstall   Uninstall the given profiles
    update      Install the latest default version of the given profiles
    cleanup     Cleanup the locally installed profiles
@@ -370,6 +372,36 @@
  -v=false
    Print verbose output.
 
+Jiri profile os-packages - List the commands to install the OS packages required by the given profiles
+
+List or optionally run the commands to install the OS packages required by the
+given profiles.
+
+Usage:
+   jiri profile os-packages [flags] <profiles>
+
+<profiles> is a list of profiles to list OS packages for.
+
+The jiri profile os-packages flags are:
+ -all=false
+   print commands to install all required OS packages, not just those that are
+   missing
+ -env=
+   specify an environment variable in the form: <var>=[<val>],...
+ -install=false
+   install the requested packages. This may need to be run as root.
+ -profiles-db=$JIRI_ROOT/.jiri_root/profile_db
+   the path, relative to JIRI_ROOT, that contains the profiles database.
+ -profiles-dir=.jiri_root/profiles
+   the directory, relative to JIRI_ROOT, that profiles are installed in
+ -target=<runtime.GOARCH>-<runtime.GOOS>
+   specifies a profile target in the following form: <arch>-<os>[@<version>]
+
+ -color=true
+   Use color to format output.
+ -v=false
+   Print verbose output.
+
 Jiri profile uninstall - Uninstall the given profiles
 
 Uninstall the given profiles.
@@ -1288,6 +1320,8 @@
 
 The jiri profile-v23 commands are:
    install     Install the given profiles
+   os-packages List the commands to install the OS packages required by the
+               given profiles
    uninstall   Uninstall the given profiles
    update      Install the latest default version of the given profiles
    cleanup     Cleanup the locally installed profiles
@@ -1331,6 +1365,43 @@
  -v=false
    Print verbose output.
 
+Jiri profile-v23 os-packages - List the commands to install the OS packages
+required by the given profiles
+
+List or optionally run the commands to install the OS packages required by the
+given profiles.
+
+Usage:
+   jiri profile-v23 os-packages [flags] <profiles>
+
+<profiles> is a list of profiles to list OS packages for.
+
+The jiri profile-v23 os-packages flags are:
+ -all=false
+   print commands to install all required OS packages, not just those that are
+   missing
+ -env=
+   specify an environment variable in the form: <var>=[<val>],...
+ -go.sysroot-image=
+   sysroot image for cross compiling to the currently specified target
+ -go.sysroot-image-dirs-to-use=/lib:/usr/lib:/usr/include
+   a colon separated list of directories to use from the sysroot image
+ -install=false
+   install the requested packages. This may need to be run as root.
+ -mojodev.dir=
+   Path of mojo repo checkout.
+ -profiles-db=$JIRI_ROOT/.jiri_root/profile_db
+   the path, relative to JIRI_ROOT, that contains the profiles database.
+ -profiles-dir=.jiri_root/profiles
+   the directory, relative to JIRI_ROOT, that profiles are installed in
+ -target=<runtime.GOARCH>-<runtime.GOOS>
+   specifies a profile target in the following form: <arch>-<os>[@<version>]
+
+ -color=true
+   Use color to format output.
+ -v=false
+   Print verbose output.
+
 Jiri profile-v23 uninstall - Uninstall the given profiles
 
 Uninstall the given profiles.