Repository: conan-io/conan

Issue:
[bug] check_output_runner in conans/utils/runner.py doesn't quote the tmp_file properly.
### Environment Details (include every applicable attribute)
  * Operating System+version: windows
  * Compiler+version: not applicable
  * Conan version: issue looks like it came in with fb2881e5bbdc9a5d2e45923a275781ef3d1c4f08 
  * Python version: not applicable

### Steps to reproduce (Include if Applicable)

At least on windows:

set TEMP=C:\Path With Spaces\
python build.py # [1] 

This is slightly problematic because in same cases windows will default TEMP directory to be inside userprofile, which in same cases is `C:\Users\User Name\AppData\Local\Temp`


### Logs (Executed commands with output) (Include/Attach if Applicable)

```
Conan:vcvars already set
Traceback (most recent call last):
  File "build.py", line 7, in <module>
    builder.run()
  File "C:\Program Files\Python37\lib\site-packages\cpt\packager.py", line 597, in run
    self.run_builds(base_profile_name=base_profile_name)
  File "C:\Program Files\Python37\lib\site-packages\cpt\packager.py", line 690, in run_builds
    r.run()
  File "C:\Program Files\Python37\lib\site-packages\cpt\runner.py", line 86, in run
    with context:
  File "C:\Program Files\Python37\lib\contextlib.py", line 112, in __enter__
    return next(self.gen)
  File "C:\Program Files\Python37\lib\site-packages\conans\tools.py", line 200, in vcvars
    with tools_win.vcvars(output=_global_output, *args, **kwargs):
  File "C:\Program Files\Python37\lib\contextlib.py", line 112, in __enter__
    return next(self.gen)
  File "C:\Program Files\Python37\lib\site-packages\conans\client\tools\win.py", line 493, in vcvars
    new_env = vcvars_dict(*args, **kwargs)
  File "C:\Program Files\Python37\lib\site-packages\conans\client\tools\win.py", line 444, in vcvars_dict
    ret = check_output_runner(cmd)
  File "C:\Program Files\Python37\lib\site-packages\conans\util\runners.py", line 92, in check_output_runner
    raise CalledProcessErrorWithStderr(process.returncode, cmd, output=stderr)
conans.errors.CalledProcessErrorWithStderr: Command 'echo Conan:vcvars already set && set' returned non-zero exit status 1.
Environment variable Mikkonen\AppData\Local\Temp\tmplzxq9vhi not defined
```

### bug

https://github.com/conan-io/conan/blob/develop/conans/util/runners.py#L84

`tmp_file` gets initialized with value `C:\Users\Jani Mikkonen\AppData\Local\Temp\tmplzxq9vhi` and the final command ends up as `something something > C:\Users\Jani Mikkonen\AppData\Local\Temp\tmplzxq9vhi` 

Command execution goes into file: `C:\Users\Jani` and exitcode of the shell call is set to 1 and thus, causing the above mentioned exception.

### Fix:

I was thinking of using `shlex.quote()` or `pipes.quote()` but both of these will use *single quotes* which do not work as intended in windows/cmd shell as shell quoting has to use *double quotes*.

I can propably submit a fix for this but i'd like to get some feedback if there's a way you guys would prefer to handle this in cross platform manner..

### build.py 

```python
from cpt.packager import ConanMultiPackager
if __name__ == "__main__":
    builder = ConanMultiPackager(username="vendor")
    for build_type in ["Debug", "RelWithDebInfo", "Release"]:
        builder.add(settings={"arch": "x86_64", "build_type": build_type}, options={}, env_vars={}, build_requires={})
        builder.add(settings={"arch": "x86", "build_type": build_type}, options={}, env_vars={}, build_requires={})
    builder.run()
```
[bug] check_output_runner in conans/utils/runner.py doesn't quote the tmp_file properly.
### Environment Details (include every applicable attribute)
  * Operating System+version: windows
  * Compiler+version: not applicable
  * Conan version: issue looks like it came in with fb2881e5bbdc9a5d2e45923a275781ef3d1c4f08 
  * Python version: not applicable

### Steps to reproduce (Include if Applicable)

At least on windows:

set TEMP=C:\Path With Spaces\
python build.py # [1] 

This is slightly problematic because in same cases windows will default TEMP directory to be inside userprofile, which in same cases is `C:\Users\User Name\AppData\Local\Temp`


### Logs (Executed commands with output) (Include/Attach if Applicable)

```
Conan:vcvars already set
Traceback (most recent call last):
  File "build.py", line 7, in <module>
    builder.run()
  File "C:\Program Files\Python37\lib\site-packages\cpt\packager.py", line 597, in run
    self.run_builds(base_profile_name=base_profile_name)
  File "C:\Program Files\Python37\lib\site-packages\cpt\packager.py", line 690, in run_builds
    r.run()
  File "C:\Program Files\Python37\lib\site-packages\cpt\runner.py", line 86, in run
    with context:
  File "C:\Program Files\Python37\lib\contextlib.py", line 112, in __enter__
    return next(self.gen)
  File "C:\Program Files\Python37\lib\site-packages\conans\tools.py", line 200, in vcvars
    with tools_win.vcvars(output=_global_output, *args, **kwargs):
  File "C:\Program Files\Python37\lib\contextlib.py", line 112, in __enter__
    return next(self.gen)
  File "C:\Program Files\Python37\lib\site-packages\conans\client\tools\win.py", line 493, in vcvars
    new_env = vcvars_dict(*args, **kwargs)
  File "C:\Program Files\Python37\lib\site-packages\conans\client\tools\win.py", line 444, in vcvars_dict
    ret = check_output_runner(cmd)
  File "C:\Program Files\Python37\lib\site-packages\conans\util\runners.py", line 92, in check_output_runner
    raise CalledProcessErrorWithStderr(process.returncode, cmd, output=stderr)
conans.errors.CalledProcessErrorWithStderr: Command 'echo Conan:vcvars already set && set' returned non-zero exit status 1.
Environment variable Mikkonen\AppData\Local\Temp\tmplzxq9vhi not defined
```

### bug

https://github.com/conan-io/conan/blob/develop/conans/util/runners.py#L84

`tmp_file` gets initialized with value `C:\Users\Jani Mikkonen\AppData\Local\Temp\tmplzxq9vhi` and the final command ends up as `something something > C:\Users\Jani Mikkonen\AppData\Local\Temp\tmplzxq9vhi` 

Command execution goes into file: `C:\Users\Jani` and exitcode of the shell call is set to 1 and thus, causing the above mentioned exception.

### Fix:

I was thinking of using `shlex.quote()` or `pipes.quote()` but both of these will use *single quotes* which do not work as intended in windows/cmd shell as shell quoting has to use *double quotes*.

I can propably submit a fix for this but i'd like to get some feedback if there's a way you guys would prefer to handle this in cross platform manner..

### build.py 

```python
from cpt.packager import ConanMultiPackager
if __name__ == "__main__":
    builder = ConanMultiPackager(username="vendor")
    for build_type in ["Debug", "RelWithDebInfo", "Release"]:
        builder.add(settings={"arch": "x86_64", "build_type": build_type}, options={}, env_vars={}, build_requires={})
        builder.add(settings={"arch": "x86", "build_type": build_type}, options={}, env_vars={}, build_requires={})
    builder.run()
```


Discussion:
Hi, @rasjani , why not using double quotes for the `tmp_file`?

```python
command = '{} > "{}"'.format(cmd, tmp_file)
```
I was just wondering why shlex.quote() doens't use double quotes by default that there would actually be some valid reason for not defaulting to those instead of single quotes.

Ill submit a patch.. 
Hi, @rasjani , why not using double quotes for the `tmp_file`?

```python
command = '{} > "{}"'.format(cmd, tmp_file)
```
I was just wondering why shlex.quote() doens't use double quotes by default that there would actually be some valid reason for not defaulting to those instead of single quotes.

Ill submit a patch.. 

Resolving patch:
diff --git a/conans/util/runners.py b/conans/util/runners.py
--- a/conans/util/runners.py
+++ b/conans/util/runners.py
@@ -81,7 +81,7 @@ def check_output_runner(cmd, stderr=None):
         # We don't want stderr to print warnings that will mess the pristine outputs
         stderr = stderr or subprocess.PIPE
         cmd = cmd if isinstance(cmd, six.string_types) else subprocess.list2cmdline(cmd)
-        command = "{} > {}".format(cmd, tmp_file)
+        command = '{} > "{}"'.format(cmd, tmp_file)
         logger.info("Calling command: {}".format(command))
         process = subprocess.Popen(command, shell=True, stderr=stderr)
         stdout, stderr = process.communicate()


Test patch:
diff --git a/conans/test/unittests/util/tools_test.py b/conans/test/unittests/util/tools_test.py
--- a/conans/test/unittests/util/tools_test.py
+++ b/conans/test/unittests/util/tools_test.py
@@ -729,6 +729,17 @@ def get_file():
 
         thread.stop()
 
+    def check_output_runner_test(self):
+        import tempfile
+        original_temp = tempfile.gettempdir()
+        patched_temp = os.path.join(original_temp, "dir with spaces")
+        payload = "hello world"
+        with patch("tempfile.mktemp") as mktemp:
+            mktemp.return_value = patched_temp
+            output = check_output_runner(["echo", payload], stderr=subprocess.STDOUT)
+            self.assertIn(payload, str(output))
+
+
     def unix_to_dos_unit_test(self):
 
         def save_file(contents):