Repository: mesonbuild/meson

Issue:
subproject as symbolic link triggers Sandbox violation: file outside current (sub) project.
**Describe the bug**
A subproject implemented as a symbolic link to another directory causes a false Sandbox violation.

**To Reproduce**
Create a symbolic link under subprojects/ which links to a directory containing a typical subproject with source files.

For example:

```
/top/main_project/subdir/meson.build
/top/main_project/subdir/src/main.cpp
/top/main_project/subprojects/mysubproject --> /top/main_project/subdir
```

Run 'meson setup <builddir>'
Observe:

`subprojects/mysubproject/meson.build:30:0: ERROR: Sandbox violation: Tried to grab file main.cpp outside current (sub)project.
`
The code that triggers the error is
in mesonbuild/interpreter/interpreter.py:

```
  2681         if project_root not in norm.parents:
  2682             raise InterpreterException(f'Sandbox violation: Tried to grab {inputtype} {norm.name} outside current (sub)project.')
```
based on the following values:

```
mysubproject| srcdir: <path to top-level dir> e.g  /top/main_project
mysubproject| norm: <absolute path to source file> e.g. /top/main_project/subdir/src/main.cpp
mysubproject| project_root: <absolute path to subproject mysubproject symbolic link> e.g  /top/main_project/subprojects/mysubproject
```
Note that the (sub) project_root, which is a symbolic link is not ".resolve()" 'd like the norm path is.
And the following change allows this to work as expected.

```
< 2680         project_root = Path(srcdir, self.root_subdir)
> 2680         project_root = Path(srcdir, self.root_subdir).resolve()

```

**Expected behavior**
I expect to be able to use symbolic links to subproject directories without a Sandbox violation.

**system parameters**
*  plain native build (for the same computer)
* Debian 7
* Python 3.6
* `meson --version` 0.58.0, but started with at least 0.57.2
* `ninja --version`1.8.2


Thanks
-tim


Discussion:
I am experiencing this exact same issue on macOS running meson 0.58.1
Any updates?
I'll have a look at this, i do use this feature from time to time, and it did work previously
Is work on this issue still in-progress? I saw that it was tagged for 0.63.0. Based off of the issue description and my own debugging, I'd be up for taking this on as a starter ticket.
AFAIK no one is currently working on this -- feel free to take it up yourself.
Aside: I personally wonder why simply having the subproject in the canonical location without symlinks isn't good enough, but:
- apparently @dcbaker believes it should work, and I don't have strong feelings on the topic
- the error message is admittedly rather confusing... if we don't want to support this, we should test it immediately upon invoking `subproject()` and say "error: subprojects cannot be symlinks". If we *don't* add such an error, then the sandbox violation code should take symlinks into account.
Using symlinks as subprojects was instrumental in allowing me to migrate an existing large project to meson
and I depend on them as a way to configure dependencies. 
I also appreciate the ability to link to a development version of project in place.

...  and thank you for taking this up :)

Resolving patch:
diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py
--- a/mesonbuild/interpreter/interpreter.py
+++ b/mesonbuild/interpreter/interpreter.py
@@ -2851,7 +2851,7 @@ def validate_installable_file(fpath: Path) -> bool:
             # subproject files, as long as they are scheduled to be installed.
             if validate_installable_file(norm):
                 return
-        norm = Path(srcdir, subdir, fname).resolve()
+        norm = Path(os.path.abspath(Path(srcdir, subdir, fname)))
         if os.path.isdir(norm):
             inputtype = 'directory'
         else:
diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py
--- a/unittests/allplatformstests.py
+++ b/unittests/allplatformstests.py
@@ -4263,3 +4263,16 @@ def test_custom_target_name(self):
         if self.backend is Backend.ninja:
             self.assertIn('Generating file.txt with a custom command', out)
             self.assertIn('Generating subdir/file.txt with a custom command', out)
+
+    def test_symlinked_subproject(self):
+        testdir = os.path.join(self.unit_test_dir, '106 subproject symlink')
+        subproject_dir = os.path.join(testdir, 'subprojects')
+        subproject = os.path.join(testdir, 'symlinked_subproject')
+        symlinked_subproject = os.path.join(testdir, 'subprojects', 'symlinked_subproject')
+        if not os.path.exists(subproject_dir):
+            os.mkdir(subproject_dir)
+        os.symlink(subproject, symlinked_subproject)
+        self.addCleanup(os.remove, symlinked_subproject)
+
+        self.init(testdir)
+        self.build()


Test patch:
diff --git a/test cases/unit/106 subproject symlink/main.c b/test cases/unit/106 subproject symlink/main.c
new file mode 100644
--- /dev/null
+++ b/test cases/unit/106 subproject symlink/main.c	
@@ -0,0 +1,6 @@
+extern int foo(void);
+
+int main(void)
+{
+    return foo();
+}
diff --git a/test cases/unit/106 subproject symlink/meson.build b/test cases/unit/106 subproject symlink/meson.build
new file mode 100644
--- /dev/null
+++ b/test cases/unit/106 subproject symlink/meson.build	
@@ -0,0 +1,8 @@
+project('foo', 'c')
+
+symlinked_subproject = subproject('symlinked_subproject')
+
+executable('foo',
+    sources : 'main.c',
+    dependencies : symlinked_subproject.get_variable('dep')
+)
diff --git a/test cases/unit/106 subproject symlink/symlinked_subproject/meson.build b/test cases/unit/106 subproject symlink/symlinked_subproject/meson.build
new file mode 100644
--- /dev/null
+++ b/test cases/unit/106 subproject symlink/symlinked_subproject/meson.build	
@@ -0,0 +1,3 @@
+project('symlinked_subproject', 'c', version : '1.0.0')
+
+dep = declare_dependency(sources : 'src.c')
diff --git a/test cases/unit/106 subproject symlink/symlinked_subproject/src.c b/test cases/unit/106 subproject symlink/symlinked_subproject/src.c
new file mode 100644
--- /dev/null
+++ b/test cases/unit/106 subproject symlink/symlinked_subproject/src.c	
@@ -0,0 +1,4 @@
+int foo(void)
+{
+    return 0;
+}