pw_env_setup: reduce output

If everything succeeds, don't print output. If a step fails, show the
full output for that step. Also minor shifting around of some echo
commands.

Fixed: 40
Change-Id: I9a3aa193a90cc6386bcd488a6b383cbcf1c8ef41
diff --git a/pw_env_setup/py/pw_env_setup/cargo_setup/__init__.py b/pw_env_setup/py/pw_env_setup/cargo_setup/__init__.py
index b596b75..ddb0da6 100644
--- a/pw_env_setup/py/pw_env_setup/cargo_setup/__init__.py
+++ b/pw_env_setup/py/pw_env_setup/cargo_setup/__init__.py
@@ -50,6 +50,4 @@
                 package,
             ]  # yapf: disable
 
-            print(' '.join(cmd))
-
             subprocess.check_call(cmd)
diff --git a/pw_env_setup/py/pw_env_setup/cipd_setup/update.py b/pw_env_setup/py/pw_env_setup/cipd_setup/update.py
index 5d3c4cf..3dfb9d5 100755
--- a/pw_env_setup/py/pw_env_setup/cipd_setup/update.py
+++ b/pw_env_setup/py/pw_env_setup/cipd_setup/update.py
@@ -28,6 +28,7 @@
 import shutil
 import subprocess
 import sys
+import tempfile
 
 
 def parse(argv=None):
@@ -141,8 +142,15 @@
             '-max-threads', '0',  # 0 means use CPU count.
         ]  # yapf: disable
 
-        print(*cmd, file=sys.stderr)
-        subprocess.check_call(cmd, stdout=sys.stderr)
+        # TODO(pwbug/135) Use function from common utility module.
+        with tempfile.TemporaryFile(mode='w+') as temp:
+            print(*cmd, file=temp)
+            try:
+                subprocess.check_call(cmd, stdout=temp)
+            except subprocess.CalledProcessError:
+                temp.seek(0)
+                sys.stderr.write(temp.read())
+                raise
 
         # Set environment variables so tools can later find things under, for
         # example, 'share'.
diff --git a/pw_env_setup/py/pw_env_setup/env_setup.py b/pw_env_setup/py/pw_env_setup/env_setup.py
index 755c2f3..6864b9d 100755
--- a/pw_env_setup/py/pw_env_setup/env_setup.py
+++ b/pw_env_setup/py/pw_env_setup/env_setup.py
@@ -106,9 +106,9 @@
             steps.append(('cargo', self.cargo))
 
         for name, step in steps:
-            print('Setting up {}...\n'.format(name), file=sys.stdout)
+            print('Setting up {}...'.format(name), file=sys.stderr)
             step()
-            print('\nSetting up {}...done.'.format(name), file=sys.stdout)
+            print('done.', file=sys.stderr)
 
         with open(self._shell_file, 'w') as outs:
             self._env.write(outs)
@@ -120,6 +120,7 @@
 
         package_files = glob.glob(
             os.path.join(self._setup_root, 'cipd_setup', '*.json'))
+        self._env.echo('Setting CIPD environment variables...')
         cipd_update.update(
             cipd=cipd_client,
             root_install_dir=install_dir,
@@ -127,8 +128,7 @@
             cache_dir=self._cipd_cache_dir,
             env_vars=self._env,
         )
-
-        self._env.echo('Setting CIPD environment variables...done.')
+        self._env.echo('done.')
 
     def virtualenv(self):
         """Setup virtualenv."""
@@ -159,27 +159,28 @@
 
         python = os.path.join(cipd_bin, py_executable)
 
+        self._env.echo('Setting virtualenv environment variables...')
         virtualenv_setup.install(
             venv_path=venv_path,
             requirements=[requirements],
             python=python,
             env=self._env,
         )
-
-        self._env.echo('Setting virtualenv environment variables...done.')
+        self._env.echo('done.')
 
     def host_build(self):
+        self._env.echo('Setting host_build environment variables...')
         host_build_setup.install(pw_root=self._pw_root, env=self._env)
-        self._env.echo('Setting host_build environment variables...done.')
+        self._env.echo('done.')
 
     def cargo(self):
+        self._env.echo('Setting cargo environment variables...')
         if os.environ.get('PW_CARGO_SETUP', ''):
             cargo_setup.install(pw_root=self._pw_root, env=self._env)
-            self._env.echo('Setting cargo environment variables...done.')
         else:
-            msg = 'cargo setup skipped, set PW_CARGO_SETUP to include it'
-            print(msg)
-            self._env.echo(msg)
+            self._env.echo(
+                '  cargo setup skipped, set PW_CARGO_SETUP to include it')
+        self._env.echo('done.')
 
 
 def parse(argv=None):
diff --git a/pw_env_setup/py/pw_env_setup/environment.py b/pw_env_setup/py/pw_env_setup/environment.py
index c7993fb..dc48fdf 100644
--- a/pw_env_setup/py/pw_env_setup/environment.py
+++ b/pw_env_setup/py/pw_env_setup/environment.py
@@ -155,7 +155,7 @@
         # POSIX shells parse arguments and pass to echo, but Windows seems to
         # pass the command line as is without parsing, so quoting is wrong.
         if windows:
-            outs.write('echo {}\n'.format(self.value))
+            outs.write('echo "{}"\n'.format(self.value))
         else:
             # TODO(mohrr) use shlex.quote().
             outs.write('if [ -z "${PW_ENVSETUP_QUIET:-}" ]; then\n')
diff --git a/pw_env_setup/py/pw_env_setup/host_build_setup.py b/pw_env_setup/py/pw_env_setup/host_build_setup.py
index 11c7a0f..1404c83 100644
--- a/pw_env_setup/py/pw_env_setup/host_build_setup.py
+++ b/pw_env_setup/py/pw_env_setup/host_build_setup.py
@@ -13,9 +13,29 @@
 # the License.
 """Builds and sets up environment to use host build."""
 
+from __future__ import print_function
+
 import os
 import platform
 import subprocess
+import sys
+import tempfile
+
+
+# TODO(pwbug/135) Move to common utility module.
+def _check_call(args, **kwargs):
+    stdout = kwargs.get('stdout', sys.stdout)
+
+    with tempfile.TemporaryFile(mode='w+') as temp:
+        try:
+            kwargs['stdout'] = temp
+            kwargs['stderr'] = subprocess.STDOUT
+            print(args, kwargs, file=temp)
+            subprocess.check_call(args, **kwargs)
+        except subprocess.CalledProcessError:
+            temp.seek(0)
+            stdout.write(temp.read())
+            raise
 
 
 def install(pw_root, env):
@@ -23,9 +43,7 @@
     env.prepend('PATH', os.path.join(host_dir, 'host_tools'))
 
     if platform.system() == 'Linux':
-        msg = 'skipping host tools setup--got from CIPD'
-        print(msg)
-        env.echo(msg)
+        env.echo('  skipping host tools setup--got from CIPD')
         return
 
     with env():
@@ -36,9 +54,9 @@
                 '--args=pw_target_toolchain="//pw_toolchain:host_clang_og"',
                 host_dir,
             ]
-            subprocess.check_call(gn_gen, cwd=pw_root)
+            _check_call(gn_gen, cwd=pw_root)
 
-            subprocess.check_call(['ninja', '-C', host_dir], cwd=pw_root)
+            _check_call(['ninja', '-C', host_dir], cwd=pw_root)
 
         except subprocess.CalledProcessError:
-            env.echo('warning: host tools failed to build')
+            env.echo('  warning: host tools failed to build')
diff --git a/pw_env_setup/py/pw_env_setup/virtualenv_setup/install.py b/pw_env_setup/py/pw_env_setup/virtualenv_setup/install.py
index 0bc2496..662d41d 100644
--- a/pw_env_setup/py/pw_env_setup/virtualenv_setup/install.py
+++ b/pw_env_setup/py/pw_env_setup/virtualenv_setup/install.py
@@ -19,6 +19,7 @@
 import os
 import subprocess
 import sys
+import tempfile
 
 
 def git_stdout(*args, **kwargs):
@@ -76,6 +77,22 @@
     ))
 
 
+# TODO(pwbug/135) Move to common utility module.
+def _check_call(args, **kwargs):
+    stdout = kwargs.get('stdout', sys.stdout)
+
+    with tempfile.TemporaryFile(mode='w+') as temp:
+        try:
+            kwargs['stdout'] = temp
+            kwargs['stderr'] = subprocess.STDOUT
+            print(args, kwargs, file=temp)
+            subprocess.check_call(args, **kwargs)
+        except subprocess.CalledProcessError:
+            temp.seek(0)
+            stdout.write(temp.read())
+            raise
+
+
 def install(
     venv_path,
     full_envsetup=True,
@@ -95,8 +112,6 @@
 
     pyvenv_cfg = os.path.join(venv_path, 'pyvenv.cfg')
     if full_envsetup or not os.path.exists(pyvenv_cfg):
-        print('Creating venv at', venv_path)
-
         # On Mac sometimes the CIPD Python has __PYVENV_LAUNCHER__ set to
         # point to the system Python, which causes CIPD Python to create
         # virtualenvs that reference the system Python instead of the CIPD
@@ -106,7 +121,7 @@
             del envcopy['__PYVENV_LAUNCHER__']
 
         cmd = (python, '-m', 'venv', '--clear', venv_path)
-        subprocess.check_call(cmd, env=envcopy)
+        _check_call(cmd, env=envcopy)
 
     # The bin/ directory is called Scripts/ on Windows. Don't ask.
     venv_bin = os.path.join(venv_path, 'Scripts' if os.name == 'nt' else 'bin')
@@ -120,30 +135,17 @@
 
     setup_py_files = git_list_files('setup.py', '*/setup.py', cwd=pw_root)
 
-    # If not forcing full setup, check if all expected packages are installed,
-    # ignoring versions. If they are, skip reinstalling.
-    if not full_envsetup:
-        installed = _installed_packages(venv_python)
-        required = _required_packages(requirements)
-        pw_pkgs = _pw_package_names(setup_py_files)
-
-        if required.issubset(installed) and pw_pkgs.issubset(installed):
-            print('Python packages already installed, exiting')
-            return
-
-        # Sometimes we get an error saying "Egg-link ... does not match
-        # installed location". This gets around that. The egg-link files
-        # all come from 'pw'-prefixed packages we installed with --editable.
-        # Source: https://stackoverflow.com/a/48972085
-        for egg_link in glob.glob(
-                os.path.join(venv_path,
-                             'lib/python*/site-packages/*.egg-link')):
-            os.unlink(egg_link)
+    # Sometimes we get an error saying "Egg-link ... does not match
+    # installed location". This gets around that. The egg-link files
+    # all come from 'pw'-prefixed packages we installed with --editable.
+    # Source: https://stackoverflow.com/a/48972085
+    for egg_link in glob.glob(
+            os.path.join(venv_path, 'lib/python*/site-packages/*.egg-link')):
+        os.unlink(egg_link)
 
     def pip_install(*args):
         cmd = [venv_python, '-m', 'pip', 'install'] + list(args)
-        print(' '.join(cmd))
-        return subprocess.check_call(cmd)
+        return _check_call(cmd)
 
     pip_install('--upgrade', 'pip')