Convert configure command path and prefix to unix style on windows

On Cygwin, MSYS2 and GitBash, the configure command and the prefix
should be converted to unix style path by cygpath command,
because the colon in the drive letter breaks many configure scripts.

This also fixes bugs of previous approach where leading slash was
missing from prefix and it sometimes use wrong directory as base path.
In addition removing drive letter should be done on rel_prefix only.
This commit is contained in:
na-trium-144
2025-07-31 16:09:51 +09:00
committed by Paolo Bonzini
parent 57820c92ea
commit 9de3a8fddd
3 changed files with 44 additions and 7 deletions

View File

@@ -0,0 +1,7 @@
## The external_project module uses the cygpath command to convert paths
In previous versions, the external_project module on Windows used a Windows-style path (e.g., `C:/path/to/configure`) to execute the configure file, and a relative path from the drive root (e.g., `/path/to/prefix`) as the installation prefix.
However, since configure scripts are typically intended to be run in a POSIX-like environment (MSYS2, Cygwin, or GitBash), these paths were incompatible with some configure scripts.
The external_project module now uses the `cygpath` command to convert the configure command path and prefix to Unix-style paths (e.g., `/c/path/to/configure` for MSYS2 and `/cygdrive/c/path/to/configure` for Cygwin).
If the `cygpath` command is not found in the PATH, it will fall back to the previous behavior.

View File

@@ -6,6 +6,7 @@ from __future__ import annotations
from pathlib import Path
import os
import shlex
import shutil
import subprocess
import typing as T
@@ -19,7 +20,7 @@ from ..interpreterbase import FeatureNew
from ..interpreter.type_checking import ENV_KW, DEPENDS_KW
from ..interpreterbase.decorators import ContainerTypeInfo, KwargInfo, typed_kwargs, typed_pos_args
from ..mesonlib import (EnvironmentException, MesonException, Popen_safe, MachineChoice,
get_variable_regex, do_replacement, join_args, relpath)
get_variable_regex, do_replacement, join_args)
from ..options import OptionKey
if T.TYPE_CHECKING:
@@ -89,19 +90,29 @@ class ExternalProject(NewExtensionModule):
self.includedir = Path(_i)
self.name = self.src_dir.name
# On Windows if the prefix is "c:/foo" and DESTDIR is "c:/bar", `make`
# will install files into "c:/bar/c:/foo" which is an invalid path.
# Work around that issue by removing the drive from prefix.
if self.prefix.drive:
self.prefix = Path(relpath(self.prefix, self.prefix.drive))
self.prefix = self._cygpath_convert(self.prefix)
# self.prefix is an absolute path, so we cannot append it to another path.
self.rel_prefix = Path(relpath(self.prefix, self.prefix.root))
# On Windows (where cygpath is not applied),
# if the prefix is "c:/foo" and DESTDIR is "c:/bar",
# `make` will install files into "c:/bar/c:/foo" which is an invalid path.
# This also removes the drive letter from the prefix to workaround the issue.
self.rel_prefix = self.prefix.relative_to(self.prefix.anchor)
self._configure(state)
self.targets = self._create_targets(extra_depends)
def _cygpath_convert(self, winpath: Path) -> Path:
# On Cygwin, MSYS2 and GitBash, the configure command and the prefix
# should be converted to unix style path like "/c/foo" by cygpath command,
# because the colon in the drive letter breaks many configure scripts.
# Do nothing on other environment where cygpath is not available.
if winpath.drive and shutil.which('cygpath'):
_p, o, _e = Popen_safe(['cygpath', '-u', winpath.as_posix()])
return Path(o.strip('\n'))
return winpath
def _configure(self, state: 'ModuleState') -> None:
if self.configure_command == 'waf':
FeatureNew('Waf external project', '0.60.0').use(self.subproject, state.current_node)
@@ -116,6 +127,8 @@ class ExternalProject(NewExtensionModule):
configure_path = Path(self.src_dir, self.configure_command)
configure_prog = state.find_program(configure_path.as_posix())
configure_cmd = configure_prog.get_command()
if len(configure_cmd) >= 2 and configure_cmd[-1] == configure_path.as_posix():
configure_cmd = configure_cmd[:-1] + [self._cygpath_convert(configure_path).as_posix()]
workdir = self.build_dir
self.make = state.find_program('make').get_command()

View File

@@ -2,6 +2,23 @@
srcdir=$(dirname "$0")
# some configure scripts seem to iterate over srcdir and other paths
# with for-loop using path_separator (most cases colon.)
PATH_SEPARATOR=:
(PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
(PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
PATH_SEPARATOR=';'
}
IFS=$PATH_SEPARATOR
for i in $srcdir
do
if [ "$i" != "$srcdir" ]
then
echo "failed to extract $srcdir using path separator $PATH_SEPARATOR"
exit 1
fi
done
for i in "$@"
do
case $i in