Improved support for projects located on a network share // Resolve #3417 , Resolve #3926 , Resolve #4102

This commit is contained in:
Ivan Kravets
2021-11-12 15:17:25 +02:00
parent 001f075a49
commit 4687665ff3
18 changed files with 67 additions and 46 deletions

View File

@ -11,6 +11,7 @@ PlatformIO Core 5
5.2.4 (2021-??-??) 5.2.4 (2021-??-??)
~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~
- Improved support for projects located on a network share (`issue #3417 <https://github.com/platformio/platformio-core/issues/3417>`_, `issue #3926 <https://github.com/platformio/platformio-core/issues/3926>`_, `issue #4099 <https://github.com/platformio/platformio-core/issues/4099>`_)
- Fixed an issue with the CLion project generator when a macro contains a space (`issue #4102 <https://github.com/platformio/platformio-core/issues/4102>`_) - Fixed an issue with the CLion project generator when a macro contains a space (`issue #4102 <https://github.com/platformio/platformio-core/issues/4102>`_)
5.2.3 (2021-11-05) 5.2.3 (2021-11-05)

View File

@ -31,7 +31,7 @@ from platformio.project.helpers import get_default_projects_dir
def projects_dir_validate(projects_dir): def projects_dir_validate(projects_dir):
assert os.path.isdir(projects_dir) assert os.path.isdir(projects_dir)
return os.path.realpath(projects_dir) return os.path.abspath(projects_dir)
DEFAULT_SETTINGS = { DEFAULT_SETTINGS = {

View File

@ -128,15 +128,20 @@ env.Replace(
], ],
) )
if ( if int(ARGUMENTS.get("ISATTY", 0)):
compat.IS_WINDOWS # pylint: disable=protected-access
and sys.version_info >= (3, 8) click._compat.isatty = lambda stream: True
and env["PROJECT_DIR"].startswith("\\\\")
): if compat.IS_WINDOWS and sys.version_info >= (3, 8) and os.getcwd().startswith("\\\\"):
click.secho("!!! WARNING !!!\t\t" * 3, fg="red")
click.secho( click.secho(
"There is a known issue with Python 3.8+ and mapped network drives on " "Your project is located on a mapped network drive but the "
"Windows.\nSee a solution at:\n" "current command-line shell does not support the UNC paths.",
"https://github.com/platformio/platformio-core/issues/3417", fg="yellow",
)
click.secho(
"Please move your project to a physical drive or check this workaround: "
"https://bit.ly/3kuU5mP\n",
fg="yellow", fg="yellow",
) )
@ -145,10 +150,6 @@ if env.subst("$BUILD_CACHE_DIR"):
os.makedirs(env.subst("$BUILD_CACHE_DIR")) os.makedirs(env.subst("$BUILD_CACHE_DIR"))
env.CacheDir("$BUILD_CACHE_DIR") env.CacheDir("$BUILD_CACHE_DIR")
if int(ARGUMENTS.get("ISATTY", 0)):
# pylint: disable=protected-access
click._compat.isatty = lambda stream: True
is_clean_all = "cleanall" in COMMAND_LINE_TARGETS is_clean_all = "cleanall" in COMMAND_LINE_TARGETS
if env.GetOption("clean") or is_clean_all: if env.GetOption("clean") or is_clean_all:
env.PioClean(is_clean_all) env.PioClean(is_clean_all)

View File

@ -32,14 +32,14 @@ def _dump_includes(env):
env.subst("$PROJECT_SRC_DIR"), env.subst("$PROJECT_SRC_DIR"),
] ]
includes["build"].extend( includes["build"].extend(
[os.path.realpath(env.subst(item)) for item in env.get("CPPPATH", [])] [os.path.abspath(env.subst(item)) for item in env.get("CPPPATH", [])]
) )
# installed libs # installed libs
includes["compatlib"] = [] includes["compatlib"] = []
for lb in env.GetLibBuilders(): for lb in env.GetLibBuilders():
includes["compatlib"].extend( includes["compatlib"].extend(
[os.path.realpath(inc) for inc in lb.get_include_dirs()] [os.path.abspath(inc) for inc in lb.get_include_dirs()]
) )
# includes from toolchains # includes from toolchains
@ -56,9 +56,7 @@ def _dump_includes(env):
os.path.join(toolchain_dir, "*", "include*"), os.path.join(toolchain_dir, "*", "include*"),
] ]
for g in toolchain_incglobs: for g in toolchain_incglobs:
includes["toolchain"].extend( includes["toolchain"].extend([os.path.abspath(inc) for inc in glob.glob(g)])
[os.path.realpath(inc) for inc in glob.glob(g)]
)
# include Unity framework if there are tests in project # include Unity framework if there are tests in project
includes["unity"] = [] includes["unity"] = []
@ -132,7 +130,7 @@ def _dump_defines(env):
def _get_svd_path(env): def _get_svd_path(env):
svd_path = env.GetProjectOption("debug_svd_path") svd_path = env.GetProjectOption("debug_svd_path")
if svd_path: if svd_path:
return os.path.realpath(svd_path) return os.path.abspath(svd_path)
if "BOARD" not in env: if "BOARD" not in env:
return None return None
@ -147,7 +145,7 @@ def _get_svd_path(env):
# default file from ./platform/misc/svd folder # default file from ./platform/misc/svd folder
p = env.PioPlatform() p = env.PioPlatform()
if os.path.isfile(os.path.join(p.get_dir(), "misc", "svd", svd_path)): if os.path.isfile(os.path.join(p.get_dir(), "misc", "svd", svd_path)):
return os.path.realpath(os.path.join(p.get_dir(), "misc", "svd", svd_path)) return os.path.abspath(os.path.join(p.get_dir(), "misc", "svd", svd_path))
return None return None

View File

@ -125,7 +125,7 @@ class LibBuilderBase(object):
def __init__(self, env, path, manifest=None, verbose=False): def __init__(self, env, path, manifest=None, verbose=False):
self.env = env.Clone() self.env = env.Clone()
self.envorigin = env.Clone() self.envorigin = env.Clone()
self.path = os.path.realpath(env.subst(path)) self.path = os.path.abspath(env.subst(path))
self.verbose = verbose self.verbose = verbose
try: try:
@ -290,7 +290,7 @@ class LibBuilderBase(object):
if self.extra_script: if self.extra_script:
self.env.SConscriptChdir(1) self.env.SConscriptChdir(1)
self.env.SConscript( self.env.SConscript(
os.path.realpath(self.extra_script), os.path.abspath(self.extra_script),
exports={"env": self.env, "pio_lib_builder": self}, exports={"env": self.env, "pio_lib_builder": self},
) )
self.env.ProcessUnFlags(self.build_unflags) self.env.ProcessUnFlags(self.build_unflags)
@ -750,14 +750,14 @@ class PlatformIOLibBuilder(LibBuilderBase):
def include_dir(self): def include_dir(self):
if "includeDir" in self._manifest.get("build", {}): if "includeDir" in self._manifest.get("build", {}):
with fs.cd(self.path): with fs.cd(self.path):
return os.path.realpath(self._manifest.get("build").get("includeDir")) return os.path.abspath(self._manifest.get("build").get("includeDir"))
return LibBuilderBase.include_dir.fget(self) # pylint: disable=no-member return LibBuilderBase.include_dir.fget(self) # pylint: disable=no-member
@property @property
def src_dir(self): def src_dir(self):
if "srcDir" in self._manifest.get("build", {}): if "srcDir" in self._manifest.get("build", {}):
with fs.cd(self.path): with fs.cd(self.path):
return os.path.realpath(self._manifest.get("build").get("srcDir")) return os.path.abspath(self._manifest.get("build").get("srcDir"))
return LibBuilderBase.src_dir.fget(self) # pylint: disable=no-member return LibBuilderBase.src_dir.fget(self) # pylint: disable=no-member
@property @property
@ -1024,7 +1024,7 @@ def GetLibBuilders(env): # pylint: disable=too-many-branches
found_incompat = False found_incompat = False
for storage_dir in env.GetLibSourceDirs(): for storage_dir in env.GetLibSourceDirs():
storage_dir = os.path.realpath(storage_dir) storage_dir = os.path.abspath(storage_dir)
if not os.path.isdir(storage_dir): if not os.path.isdir(storage_dir):
continue continue
for item in sorted(os.listdir(storage_dir)): for item in sorted(os.listdir(storage_dir)):

View File

@ -376,7 +376,7 @@ def GetExtraScripts(env, scope):
if not items: if not items:
return items return items
with fs.cd(env.subst("$PROJECT_DIR")): with fs.cd(env.subst("$PROJECT_DIR")):
return [os.path.realpath(item) for item in items] return [os.path.abspath(item) for item in items]
def exists(_): def exists(_):

View File

@ -207,12 +207,12 @@ def ParseFlagsExtended(env, flags): # pylint: disable=too-many-branches
for k in ("CPPPATH", "LIBPATH"): for k in ("CPPPATH", "LIBPATH"):
for i, p in enumerate(result.get(k, [])): for i, p in enumerate(result.get(k, [])):
if os.path.isdir(p): if os.path.isdir(p):
result[k][i] = os.path.realpath(p) result[k][i] = os.path.abspath(p)
# fix relative path for "-include" # fix relative path for "-include"
for i, f in enumerate(result.get("CCFLAGS", [])): for i, f in enumerate(result.get("CCFLAGS", [])):
if isinstance(f, tuple) and f[0] == "-include": if isinstance(f, tuple) and f[0] == "-include":
result["CCFLAGS"][i] = (f[0], env.File(os.path.realpath(f[1].get_path()))) result["CCFLAGS"][i] = (f[0], env.File(os.path.abspath(f[1].get_path())))
return result return result

View File

@ -86,7 +86,7 @@ class DefectItem(object):
"severity": self.SEVERITY_LABELS[self.severity], "severity": self.SEVERITY_LABELS[self.severity],
"category": self.category, "category": self.category,
"message": self.message, "message": self.message,
"file": os.path.realpath(self.file), "file": os.path.abspath(self.file),
"line": self.line, "line": self.line,
"column": self.column, "column": self.column,
"callstack": self.callstack, "callstack": self.callstack,

View File

@ -201,11 +201,11 @@ class CheckToolBase(object): # pylint: disable=too-many-instance-attributes
def _add_file(path): def _add_file(path):
if path.endswith(header_extensions): if path.endswith(header_extensions):
result["headers"].append(os.path.realpath(path)) result["headers"].append(os.path.abspath(path))
elif path.endswith(c_extension): elif path.endswith(c_extension):
result["c"].append(os.path.realpath(path)) result["c"].append(os.path.abspath(path))
elif path.endswith(cpp_extensions): elif path.endswith(cpp_extensions):
result["c++"].append(os.path.realpath(path)) result["c++"].append(os.path.abspath(path))
for pattern in patterns: for pattern in patterns:
for item in glob.glob(pattern, recursive=True): for item in glob.glob(pattern, recursive=True):

View File

@ -33,7 +33,7 @@ def validate_path(ctx, param, value): # pylint: disable=unused-argument
for i, p in enumerate(value): for i, p in enumerate(value):
if p.startswith("~"): if p.startswith("~"):
value[i] = fs.expanduser(p) value[i] = fs.expanduser(p)
value[i] = os.path.realpath(value[i]) value[i] = os.path.abspath(value[i])
if not glob.glob(value[i], recursive=True): if not glob.glob(value[i], recursive=True):
invalid_path = p invalid_path = p
break break
@ -162,7 +162,7 @@ def _exclude_contents(dst_dir, patterns):
for p in patterns: for p in patterns:
contents += glob.glob(os.path.join(glob.escape(dst_dir), p), recursive=True) contents += glob.glob(os.path.join(glob.escape(dst_dir), p), recursive=True)
for path in contents: for path in contents:
path = os.path.realpath(path) path = os.path.abspath(path)
if os.path.isdir(path): if os.path.isdir(path):
fs.rmtree(path) fs.rmtree(path)
elif os.path.isfile(path): elif os.path.isfile(path):

View File

@ -93,7 +93,7 @@ class ProjectRPC:
# skip non existing folders and resolve full path # skip non existing folders and resolve full path
for key in ("envLibdepsDirs", "libExtraDirs"): for key in ("envLibdepsDirs", "libExtraDirs"):
data[key] = [ data[key] = [
fs.expanduser(d) if d.startswith("~") else os.path.realpath(d) fs.expanduser(d) if d.startswith("~") else os.path.abspath(d)
for d in data[key] for d in data[key]
if os.path.isdir(d) if os.path.isdir(d)
] ]

View File

@ -61,7 +61,7 @@ class GDBClientProcess(DebugClientProcess):
def _get_data_dir(gdb_path): def _get_data_dir(gdb_path):
if "msp430" in gdb_path: if "msp430" in gdb_path:
return None return None
gdb_data_dir = os.path.realpath( gdb_data_dir = os.path.abspath(
os.path.join(os.path.dirname(gdb_path), "..", "share", "gdb") os.path.join(os.path.dirname(gdb_path), "..", "share", "gdb")
) )
return gdb_data_dir if os.path.isdir(gdb_data_dir) else None return gdb_data_dir if os.path.isdir(gdb_data_dir) else None

View File

@ -24,7 +24,7 @@ import sys
import click import click
from platformio import exception from platformio import exception, proc
from platformio.compat import IS_WINDOWS from platformio.compat import IS_WINDOWS
@ -41,7 +41,7 @@ class cd(object):
def get_source_dir(): def get_source_dir():
curpath = os.path.realpath(__file__) curpath = os.path.abspath(__file__)
if not os.path.isfile(curpath): if not os.path.isfile(curpath):
for p in sys.path: for p in sys.path:
if os.path.isfile(os.path.join(p, __file__)): if os.path.isfile(os.path.join(p, __file__)):
@ -119,7 +119,7 @@ def ensure_udev_rules():
if not any(os.path.isfile(p) for p in installed_rules): if not any(os.path.isfile(p) for p in installed_rules):
raise exception.MissedUdevRules raise exception.MissedUdevRules
origin_path = os.path.realpath( origin_path = os.path.abspath(
os.path.join(get_source_dir(), "..", "scripts", "99-platformio-udev.rules") os.path.join(get_source_dir(), "..", "scripts", "99-platformio-udev.rules")
) )
if not os.path.isfile(origin_path): if not os.path.isfile(origin_path):
@ -181,6 +181,25 @@ def to_unix_path(path):
return re.sub(r"[\\]+", "/", path) return re.sub(r"[\\]+", "/", path)
def normalize_path(path):
path = os.path.abspath(path)
if not IS_WINDOWS or not path.startswith("\\\\"):
return path
try:
result = proc.exec_command(["net", "use"])
if result["returncode"] != 0:
return path
share_re = re.compile(r"\s([A-Z]\:)\s+(\\\\[^\s]+)")
for line in result["out"].split("\n"):
share = share_re.search(line)
if not share:
continue
path = path.replace(share.group(2), share.group(1))
except OSError:
pass
return path
def expanduser(path): def expanduser(path):
""" """
Be compatible with Python 3.8, on Windows skip HOME and check for USERPROFILE Be compatible with Python 3.8, on Windows skip HOME and check for USERPROFILE

View File

@ -48,7 +48,7 @@ class LockFile(object):
def __init__(self, path, timeout=LOCKFILE_TIMEOUT, delay=LOCKFILE_DELAY): def __init__(self, path, timeout=LOCKFILE_TIMEOUT, delay=LOCKFILE_DELAY):
self.timeout = timeout self.timeout = timeout
self.delay = delay self.delay = delay
self._lock_path = os.path.realpath(path) + ".lock" self._lock_path = os.path.abspath(path) + ".lock"
self._fp = None self._fp = None
def _lock(self): def _lock(self):

View File

@ -252,9 +252,9 @@ class BasePackageManager( # pylint: disable=too-many-public-methods
# external "URL" mismatch # external "URL" mismatch
if spec.external: if spec.external:
# local folder mismatch # local folder mismatch
if os.path.realpath(spec.url) == os.path.realpath(pkg.path) or ( if os.path.abspath(spec.url) == os.path.abspath(pkg.path) or (
spec.url.startswith("file://") spec.url.startswith("file://")
and os.path.realpath(pkg.path) == os.path.realpath(spec.url[7:]) and os.path.abspath(pkg.path) == os.path.abspath(spec.url[7:])
): ):
return True return True
if spec.url != pkg.metadata.spec.url: if spec.url != pkg.metadata.spec.url:

View File

@ -82,7 +82,7 @@ class ProjectGenerator(object):
"project_dir": self.project_dir, "project_dir": self.project_dir,
"original_env_name": self.original_env_name, "original_env_name": self.original_env_name,
"env_name": self.env_name, "env_name": self.env_name,
"user_home_dir": os.path.realpath(fs.expanduser("~")), "user_home_dir": os.path.abspath(fs.expanduser("~")),
"platformio_path": sys.argv[0] "platformio_path": sys.argv[0]
if os.path.isfile(sys.argv[0]) if os.path.isfile(sys.argv[0])
else where_is_program("platformio"), else where_is_program("platformio"),
@ -125,7 +125,9 @@ class ProjectGenerator(object):
with fs.cd(self.project_dir): with fs.cd(self.project_dir):
for root, _, files in os.walk(self.config.get("platformio", "src_dir")): for root, _, files in os.walk(self.config.get("platformio", "src_dir")):
for f in files: for f in files:
result.append(os.path.relpath(os.path.join(root, f))) result.append(
os.path.relpath(os.path.join(os.path.realpath(root), f))
)
return result return result
def get_tpls(self): def get_tpls(self):

View File

@ -24,7 +24,7 @@ from platformio.project.config import ProjectConfig
def get_project_dir(): def get_project_dir():
return os.getcwd() return fs.normalize_path(os.getcwd())
def is_platformio_project(project_dir=None): def is_platformio_project(project_dir=None):

View File

@ -114,7 +114,7 @@ def validate_dir(path):
path = fs.expanduser(path) path = fs.expanduser(path)
if "$" in path: if "$" in path:
path = expand_dir_templates(path) path = expand_dir_templates(path)
return os.path.realpath(path) return fs.normalize_path(path)
def validate_core_dir(path): def validate_core_dir(path):