Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor subsystem code #10743

Merged
merged 4 commits into from Mar 11, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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, subsystem=None):
lasote marked this conversation as resolved.
Show resolved Hide resolved
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 = subsystem or 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