Skip to content

Commit

Permalink
Refactor subsystem code (#10743)
Browse files Browse the repository at this point in the history
* Refactor subsystem code

* Remove local import

* Removed argument

* Add tool test
  • Loading branch information
lasote committed Mar 11, 2022
1 parent 8311b40 commit ae66343
Show file tree
Hide file tree
Showing 10 changed files with 217 additions and 181 deletions.
4 changes: 1 addition & 3 deletions conan/tools/env/environment.py
Expand Up @@ -4,7 +4,7 @@
from collections import OrderedDict
from contextlib import contextmanager

from conan.tools.microsoft.subsystems import deduce_subsystem, WINDOWS
from conans.client.subsystems import deduce_subsystem, WINDOWS, subsystem_path
from conans.errors import ConanException
from conans.util.files import save

Expand All @@ -14,7 +14,6 @@ class _EnvVarPlaceHolder:


def environment_wrap_command(env_filenames, cmd, subsystem=None, cwd=None):
from conan.tools.microsoft.subsystems import subsystem_path
assert env_filenames
filenames = [env_filenames] if not isinstance(env_filenames, list) else env_filenames
bats, shs = [], []
Expand Down Expand Up @@ -131,7 +130,6 @@ def get_str(self, placeholder, subsystem, pathsep):
values.append(placeholder.format(name=self._name))
else:
if self._path:
from conan.tools.microsoft.subsystems import subsystem_path
v = subsystem_path(subsystem, v)
values.append(v)
if self._path:
Expand Down
2 changes: 1 addition & 1 deletion conan/tools/gnu/autotools.py
Expand Up @@ -2,7 +2,7 @@

from conan.tools.build import build_jobs
from conan.tools.files.files import load_toolchain_args
from conan.tools.microsoft.subsystems import subsystem_path, deduce_subsystem
from conans.client.subsystems import subsystem_path, deduce_subsystem
from conans.client.build import join_arguments


Expand Down
2 changes: 1 addition & 1 deletion conan/tools/gnu/gnudeps_flags.py
Expand Up @@ -5,7 +5,7 @@

from conan.tools.microsoft import is_msvc
from conan.tools.apple.apple import is_apple_os
from conan.tools.microsoft.subsystems import subsystem_path, deduce_subsystem
from conans.client.subsystems import subsystem_path, deduce_subsystem


class GnuDepsFlags(object):
Expand Down
2 changes: 1 addition & 1 deletion conan/tools/microsoft/__init__.py
Expand Up @@ -2,5 +2,5 @@
from conan.tools.microsoft.msbuild import MSBuild
from conan.tools.microsoft.msbuilddeps import MSBuildDeps
from conan.tools.microsoft.visual import msvc_runtime_flag, VCVars, is_msvc, is_msvc_static_runtime
from conan.tools.microsoft.subsystems import subsystem_path
from conan.tools.microsoft.layout import vs_layout
from conan.tools.microsoft.subsystems import unix_path
176 changes: 5 additions & 171 deletions conan/tools/microsoft/subsystems.py
@@ -1,174 +1,8 @@
import os
import platform
import re
import subprocess
from conans.client.subsystems import deduce_subsystem, subsystem_path

from conans.errors import ConanException

WINDOWS = "windows"
MSYS2 = 'msys2'
MSYS = 'msys'
CYGWIN = 'cygwin'
WSL = 'wsl' # Windows Subsystem for Linux
SFU = 'sfu' # Windows Services for UNIX


def run_in_windows_bash(conanfile, command, cwd=None, env=None):
from conan.tools.env import Environment
from conan.tools.env.environment import environment_wrap_command
""" Will run a unix command inside a bash terminal It requires to have MSYS2, CYGWIN, or WSL"""
if env:
# Passing env invalidates the conanfile.environment_scripts
env_win = [env] if not isinstance(env, list) else env
env_shell = []
else:
env_shell = ["conanbuild.sh"]
env_win = ["conanbuild.bat"]

subsystem = conanfile.conf.get("tools.microsoft.bash:subsystem")
shell_path = conanfile.conf.get("tools.microsoft.bash:path")

if not platform.system() == "Windows":
raise ConanException("Command only for Windows operating system")

if not subsystem or not shell_path:
raise ConanException("The config 'tools.microsoft.bash:subsystem' and 'tools.microsoft.bash:path' are "
"needed to run commands in a Windows subsystem")
if subsystem == MSYS2:
# Configure MSYS2 to inherith the PATH
msys2_mode_env = Environment()
_msystem = {"x86": "MINGW32"}.get(conanfile.settings.get_safe("arch"), "MINGW64")
msys2_mode_env.define("MSYSTEM", _msystem)
msys2_mode_env.define("MSYS2_PATH_TYPE", "inherit")
path = os.path.join(conanfile.generators_folder, "msys2_mode.bat")
msys2_mode_env.vars(conanfile, "build").save_bat(path)
env_win.append(path)

# Needed to change to that dir inside the bash shell
wrapped_shell = '"%s"' % shell_path if " " in shell_path else shell_path
if env_win:
wrapped_shell = environment_wrap_command(env_win, shell_path,
cwd=conanfile.generators_folder)

cwd = cwd or os.getcwd()
if not os.path.isabs(cwd):
cwd = os.path.join(os.getcwd(), cwd)
cwd_inside = subsystem_path(subsystem, cwd)
wrapped_user_cmd = command
if env_shell:
# Wrapping the inside_command enable to prioritize our environment, otherwise /usr/bin go
# first and there could be commands that we want to skip
wrapped_user_cmd = environment_wrap_command(env_shell, command,
cwd=conanfile.generators_folder)
inside_command = 'cd "{cwd_inside}" && ' \
'{wrapped_user_cmd}'.format(cwd_inside=cwd_inside,
wrapped_user_cmd=wrapped_user_cmd)

inside_command = escape_windows_cmd(inside_command)

final_command = 'cd "{cwd}" && {wrapped_shell} --login -c {inside_command}'.format(
cwd=cwd,
wrapped_shell=wrapped_shell,
inside_command=inside_command)
conanfile.output.info('Running in windows bash: %s' % final_command)
return conanfile._conan_runner(final_command, output=conanfile.output, subprocess=True)


def escape_windows_cmd(command):
""" To use in a regular windows cmd.exe
1. Adds escapes so the argument can be unpacked by CommandLineToArgvW()
2. Adds escapes for cmd.exe so the argument survives cmd.exe's substitutions.
Useful to escape commands to be executed in a windows bash (msys2, cygwin etc)
"""
quoted_arg = subprocess.list2cmdline([command])
return "".join(["^%s" % arg if arg in r'()%!^"<>&|' else arg for arg in quoted_arg])


def deduce_subsystem(conanfile, scope):
if scope.startswith("build"):
if hasattr(conanfile, "settings_build"):
the_os = conanfile.settings_build.get_safe("os")
subsystem = conanfile.settings_build.get_safe("os.subsystem")
else:
the_os = platform.system() # FIXME: Temporary fallback until 2.0
subsystem = None
else:
the_os = conanfile.settings.get_safe("os")
subsystem = conanfile.settings.get_safe("os.subsystem")

if not str(the_os).startswith("Windows"):
return None

if subsystem is None and not scope.startswith("build"): # "run" scope do not follow win_bash
return WINDOWS

if subsystem is None: # Not defined by settings, so native windows
if not conanfile.win_bash:
return WINDOWS

subsystem = conanfile.conf.get("tools.microsoft.bash:subsystem")
if not subsystem:
raise ConanException("The config 'tools.microsoft.bash:subsystem' is "
"needed to run commands in a Windows subsystem")
return subsystem


def subsystem_path(subsystem, path):
""""Used to translate windows paths to MSYS unix paths like
c/users/path/to/file. Not working in a regular console or MinGW!
"""
if subsystem is None or subsystem == WINDOWS:
def unix_path(conanfile, path):
if not conanfile.win_bash:
return path

if os.path.exists(path):
# if the path doesn't exist (and abs) we cannot guess the casing
path = get_cased_path(path)

if path.startswith('\\\\?\\'):
path = path[4:]
path = path.replace(":/", ":\\")
append_prefix = re.match(r'[a-z]:\\', path, re.IGNORECASE)
pattern = re.compile(r'([a-z]):\\', re.IGNORECASE)
path = pattern.sub('/\\1/', path).replace('\\', '/')

if append_prefix:
if subsystem in (MSYS, MSYS2):
return path.lower()
elif subsystem == CYGWIN:
return '/cygdrive' + path.lower()
elif subsystem == WSL:
return '/mnt' + path[0:2].lower() + path[2:]
elif subsystem == SFU:
path = path.lower()
return '/dev/fs' + path[0] + path[1:].capitalize()
else:
return path if subsystem == WSL else path.lower()
return None


def get_cased_path(name):
if platform.system() != "Windows":
return name
if not os.path.isabs(name):
name = os.path.abspath(name)

result = []
current = name
while True:
parent, child = os.path.split(current)
if parent == current:
break

child_cased = child
if os.path.exists(parent):
children = os.listdir(parent)
for c in children:
if c.upper() == child.upper():
child_cased = c
break
result.append(child_cased)
current = parent
drive, _ = os.path.splitdrive(current)
result.append(drive)
return os.sep.join(reversed(result))
subsystem = deduce_subsystem(conanfile, scope="build")
return subsystem_path(subsystem, path)
3 changes: 1 addition & 2 deletions conans/client/generators/__init__.py
Expand Up @@ -3,11 +3,11 @@
from os.path import join

from conan.tools.env import VirtualRunEnv
from conan.tools.microsoft.subsystems import deduce_subsystem
from conans.client.generators.cmake_find_package import CMakeFindPackageGenerator
from conans.client.generators.cmake_find_package_multi import CMakeFindPackageMultiGenerator
from conans.client.generators.compiler_args import CompilerArgsGenerator
from conans.client.generators.pkg_config import PkgConfigGenerator
from conans.client.subsystems import deduce_subsystem, subsystem_path
from conans.errors import ConanException, conanfile_exception_formatter
from conans.util.env_reader import get_env
from conans.util.files import normalize, save, mkdir
Expand Down Expand Up @@ -264,7 +264,6 @@ def write_toolchain(conanfile, path, output):


def _generate_aggregated_env(conanfile):
from conan.tools.microsoft.subsystems import subsystem_path

def deactivates(filenames):
# FIXME: Probably the order needs to be reversed
Expand Down

0 comments on commit ae66343

Please sign in to comment.