Repository: SCons/scons

Issue:
relative path in build option
This issue was originally created at: 2003-01-21 05:53:00.
This issue was reported by: `nbecker`.
nbecker said at 2003-01-21 05:53:00
>> Repository ('../../src/fixed') 
>> Repository ('../../src/float') 
>> env = Environment(LIBS = 'libiTest', LIBPATH = ['.'], 
>CXXFLAGS = '-g') 
>> env.Library(target = 'libiTest', source = ['polint.cc', 
>'QPSKConstellation.cc', 'ODemod.cc', 'SerialFFTFilt.cc', 
>'FFTEqualizer.cc', 'iDFTEqualizer.cc', 'TWT.cc', 'FastTWT.cc']) 
>>  
>> ALLTESTS = [ 'Test32', 'Test33' ] 
>> for test in ALLTESTS: 
>> 	env.Program(test, test+'.cc') 
>        . 
>        . 
>        . 
>> Only one problem.  I don't want the argument to c++ to 
>contain the 
>> complete file path.  If it did, the executable would be harder 
>to 
>> debug if moved to a different location.  I'd want it to say: 
>>  
>> c++ -g -c -o polint.o ../../src/float/polint.cc 
>>  
>> Any ideas? 
> 
>Unfortunately, that's just the way it works right now.  We need 
>a way to 
>specify this, probably through a '.relpath' attribute that could be 
>used 
>for $TARGET(S) and $SOURCE(S) expansion: 
> 
>        $CC -c -o ${TARGET.relpath} ${SOURCES.relpath} 
> 
>Could you open up a feature request for this?  Thanks!

issues@scons said at 2003-01-21 05:53:00
>Converted from SourceForge tracker item 671788

garyo said at 2008-03-17 18:16:32
>SHould be feasible; try for 1.x.

garyo said at 2008-03-17 18:18:07
>p3.

garyo said at 2008-03-17 18:25:39
>2.x since this would be nice but it's not a must-have feature.

gregnoel said at 2008-12-26 13:20:19
>Adjust triage of issues.


Votes for this issue: 1.



Discussion:

Resolving patch:
diff --git a/SCons/Node/FS.py b/SCons/Node/FS.py
--- a/SCons/Node/FS.py
+++ b/SCons/Node/FS.py
@@ -442,6 +442,11 @@ def __get_abspath(self):
         return SCons.Subst.SpecialAttrWrapper(entry.get_abspath(),
                                              entry.name + "_abspath")
 
+    def __get_relpath(self):
+        entry = self.get()
+        return SCons.Subst.SpecialAttrWrapper(entry.get_relpath(),
+                                             entry.name + "_relpath")
+
     def __get_filebase(self):
         name = self.get().name
         return SCons.Subst.SpecialAttrWrapper(SCons.Util.splitext(name)[0],
@@ -510,6 +515,7 @@ def __get_dir(self):
                          "srcdir"   : __get_srcdir,
                          "dir"      : __get_dir,
                          "abspath"  : __get_abspath,
+                         "relpath"  : __get_relpath,
                          "filebase" : __get_filebase,
                          "suffix"   : __get_suffix,
                          "file"     : __get_file,
@@ -830,6 +836,10 @@ def get_labspath(self):
         """Get the absolute path of the file."""
         return self.dir.entry_labspath(self.name)
 
+    def get_relpath(self):
+        """Get the path of the file relative to the root SConstruct file's directory."""
+        return os.path.relpath(self.dir.entry_abspath(self.name), self.fs.SConstruct_dir.get_abspath())
+
     def get_internal_path(self):
         if self.dir._path == '.':
             return self.name
@@ -937,10 +947,11 @@ def _glob1(self, pattern, ondisk=True, source=False, strings=False):
 
 # Dict that provides a simple backward compatibility
 # layer for the Node attributes 'abspath', 'labspath',
-# 'path', 'tpath' and 'path_elements'.
+# 'relpath', 'path', 'tpath' and 'path_elements'.
 # @see Base.__getattr__ above
 node_bwcomp = {'abspath' : Base.get_abspath,
                'labspath' : Base.get_labspath,
+               'relpath' : Base.get_relpath,
                'path' : Base.get_internal_path,
                'tpath' : Base.get_tpath,
                'path_elements' : Base.get_path_elements,
diff --git a/SCons/__init__.py b/SCons/__init__.py
--- a/SCons/__init__.py
+++ b/SCons/__init__.py
@@ -1,9 +1,9 @@
 __version__="4.1.1a"
 __copyright__="Copyright (c) 2001 - 2021 The SCons Foundation"
-__developer__="bdbaddog"
-__date__="2021-02-26 22:38:25"
-__buildsys__="ProDog2020"
-__revision__="3fa309f8fc5472fa067d7c04481fd0a5d3e86352"
-__build__="3fa309f8fc5472fa067d7c04481fd0a5d3e86352"
+__developer__="millsd"
+__date__="2021-03-18 01:11:29"
+__buildsys__="chn-cad-vnc31"
+__revision__="fc3946a217f29407c65291c5358262a7b3cc78cd"
+__build__="fc3946a217f29407c65291c5358262a7b3cc78cd"
 # make sure compatibility is always in place
 import SCons.compat # noqa
\ No newline at end of file


Test patch:
diff --git a/test/File/File-relpath.py b/test/File/File-relpath.py
new file mode 100644
--- /dev/null
+++ b/test/File/File-relpath.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python
+#
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+"""
+Test that .relpath works on file nodes.
+Specifically ${TARGET.relpath}, ${SOURCE.relpath} match expected path
+"""
+
+import os
+
+import TestSCons
+from TestCmd import IS_WINDOWS
+
+test = TestSCons.TestSCons()
+
+test.subdir("src", ["src", "dir"])
+
+test.dir_fixture("fixture/relpath")
+
+expected = [
+    # expanding variable, expected string
+    ("${TARGETS.relpath}", "../foo/dir build/file1"),
+    (
+        "${TARGETS.abspath}",
+        "%s %s"
+        % (os.path.abspath("base/../foo/dir"), os.path.abspath("base/build/file1")),
+    ),
+    ("${SOURCES.relpath}", "src/file"),
+    ("${SOURCES.abspath}", os.path.abspath("base/src/file")),
+    ("${SOURCE.relpath}", "src/file"),
+    ("${SOURCE.abspath}", os.path.abspath("base/src/file")),
+]
+
+expected_stdout = "\n".join(["%s=%s" % (s, o) for s, o in expected])
+expected_stdout += "\nscons: `.' is up to date."
+
+if IS_WINDOWS:
+    expected_stdout = expected_stdout.replace("/", os.sep)
+
+test.run("-Q", chdir="base", status=0, stdout=expected_stdout + "\n")
diff --git a/test/File.py b/test/File/File.py
similarity index 100%
rename from test/File.py
rename to test/File/File.py
diff --git a/test/File/fixture/relpath/base/SConstruct b/test/File/fixture/relpath/base/SConstruct
new file mode 100644
--- /dev/null
+++ b/test/File/fixture/relpath/base/SConstruct
@@ -0,0 +1,22 @@
+# Testcase to check that .relpath works on SOURCES,TARGETS, and the singular SOURCE
+# This is the SConstruct for test/File/File-relpath.py
+#
+DefaultEnvironment(tools=[])
+env = Environment(tools=[])
+
+input_list = [
+        "${TARGETS.relpath}",
+        "${TARGETS.abspath}",
+        "${SOURCES.relpath}",
+        "${SOURCES.abspath}",
+        "${SOURCE.relpath}",
+        "${SOURCE.abspath}",
+    ]
+outputs = env.subst(
+        input_list,
+    target=[File("../foo/dir"), File("build/file1")],
+    source=[File("src/file")],
+)
+
+for i,s in zip(input_list,outputs):
+        print("%s=%s"%(i,s))
diff --git a/test/File/fixture/relpath/base/build/file1 b/test/File/fixture/relpath/base/build/file1
new file mode 100644
diff --git a/test/File/fixture/relpath/base/src/file b/test/File/fixture/relpath/base/src/file
new file mode 100644