Skip to content

Commit

Permalink
Get include directories for CC-like compilers if possible
Browse files Browse the repository at this point in the history
This also lets us avoid unnecessarily passing default include directories on
the command line.
  • Loading branch information
jimporter committed May 26, 2023
1 parent 5c3b514 commit c727d0d
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 22 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Bug fixes
- When using the `PKG_CONFIG_PATH` specified by mopack, add it to any existing
`PKG_CONFIG_PATH` from the environment
- Don't explicitly include default `#include` paths on CC-like compilers

---

Expand Down
52 changes: 42 additions & 10 deletions bfg9000/tools/cc/compiler.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
from itertools import chain

from ... import options as opts, safe_str
from ... import options as opts, safe_str, shell
from .flags import optimize_flags
from ..common import BuildCommand
from ...file_types import ObjectFile, PrecompiledHeader
from ...iterutils import iterate
from ...path import Path
from ...iterutils import default_sentinel, iterate
from ...objutils import memoize_method
from ...path import abspath, Path
from ...versioning import SpecifierSet


Expand All @@ -22,8 +23,37 @@ def needs_libs(self):
def needs_package_options(self):
return True

@memoize_method
def _search_dirs(self, cpath=default_sentinel, strict=False):
try:
extra_env = ({'CPATH': cpath or ''}
if cpath is not default_sentinel else None)
output = self.env.execute(
(self.command + self._always_flags + self.global_flags +
['-E', '-Wp,-v', '/dev/null']),
extra_env=extra_env, stdout=shell.Mode.pipe,
stderr=shell.Mode.stdout
)

found = False
dirs = []
for i in output.splitlines(): # pragma: no branch
if i == '#include <...> search starts here:':
found = True
elif i == 'End of search list.':
break
elif i[0] != ' ':
found = False
elif found:
dirs.append(abspath(i[1:]))
return dirs
except (OSError, shell.CalledProcessError):
if strict:
raise
return self.env.variables.getpaths('CPATH')

def search_dirs(self, strict=False):
return self.env.variables.getpaths('CPATH')
return self._search_dirs(strict=strict)

def _call(self, cmd, input, output, deps=None, flags=None):
result = list(chain(
Expand All @@ -49,12 +79,14 @@ def _always_flags(self):
return flags

def _include_dir(self, directory, allow_system):
is_default = directory.path in self.env.host_platform.include_dirs

# Don't include default directories as system dirs (e.g. /usr/include).
# Doing so would break GCC 6 when #including stdlib.h:
# <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70129>.
if allow_system and directory.system and not is_default:
default_dirs = self._search_dirs(None)
if directory.path in default_dirs:
# Don't include default directories (e.g. /usr/include). Including
# them as system dirs would break GCC 6 when #including stdlib.h:
# <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70129>. Including
# them as regular directories isn't right either.
return []
elif allow_system and directory.system:
return ['-isystem', directory.path]
else:
return ['-I' + directory.path]
Expand Down
3 changes: 3 additions & 0 deletions bfg9000/tools/cc/linker.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from ...builtins.copy_file import CopyFile
from ...file_types import *
from ...iterutils import first, iterate, listify, recursive_walk, uniques
from ...objutils import memoize_method
from ...path import abspath, Path
from ...versioning import SpecifierSet
from ...packages import Framework
Expand Down Expand Up @@ -71,6 +72,7 @@ def _has_link_macros(self):
# and only define the macros if it does.
return self.env.target_platform.has_import_library

@memoize_method
def sysroot(self, strict=False):
try:
# XXX: clang doesn't support -print-sysroot.
Expand All @@ -83,6 +85,7 @@ def sysroot(self, strict=False):
raise
return '' if self.env.target_platform.family == 'windows' else '/'

@memoize_method
def search_dirs(self, strict=False):
try:
output = self.env.execute(
Expand Down
1 change: 1 addition & 0 deletions doc/about/changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ in progress
### Bug fixes
- When using the `PKG_CONFIG_PATH` specified by mopack, add it to any existing
`PKG_CONFIG_PATH` from the environment
- Don't explicitly include default `#include` paths on CC-like compilers

---

Expand Down
50 changes: 39 additions & 11 deletions test/unit/tools/cc/test_compiler.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from textwrap import dedent
from unittest import mock

from ... import *
Expand All @@ -7,7 +8,7 @@
from bfg9000.file_types import (HeaderDirectory, HeaderFile, ObjectFile,
PrecompiledHeader, SourceFile)
from bfg9000.tools.cc import CcBuilder
from bfg9000.path import Path, Root
from bfg9000.path import abspath, Path, Root


class TestCcCompiler(CrossPlatformTestCase):
Expand All @@ -19,6 +20,7 @@ def setUp(self):
mock.patch('bfg9000.shell.execute', mock_execute):
self.compiler = CcBuilder(self.env, known_langs['c++'], ['c++'],
True, 'version').compiler
self.compiler._search_dirs._reset(self.compiler)

def test_call(self):
extra = self.compiler._always_flags
Expand Down Expand Up @@ -49,24 +51,50 @@ def test_output_file(self):
self.assertEqual(self.compiler.output_file('file', None),
ObjectFile(Path('file.o'), fmt, 'c++'))

def test_search_dirs(self):
def mock_execute(*args, **kwargs):
return dedent("""\
/bad/include
#include <...> search starts here:
/usr/include
/path/to/include
End of search list.
""")

dirs = [abspath('/usr/include'), abspath('/path/to/include')]
with mock.patch('bfg9000.shell.execute', mock_execute):
self.assertEqual(self.compiler.search_dirs(), dirs)
self.assertEqual(self.compiler.search_dirs(True), dirs)

def test_search_dirs_broken(self):
def mock_execute(*args, **kwargs):
raise OSError()

with mock.patch('bfg9000.shell.execute', mock_execute):
self.assertEqual(self.compiler.search_dirs(), [])
with self.assertRaises(OSError):
self.compiler.search_dirs(True)

def test_flags_empty(self):
self.assertEqual(self.compiler.flags(opts.option_list()), [])

def test_flags_include_dir(self):
p = self.Path('/path/to/include')
self.assertEqual(self.compiler.flags(opts.option_list(
opts.include_dir(HeaderDirectory(p))
)), ['-I' + p])
search_dirs = 'bfg9000.tools.cc.compiler.CcBaseCompiler._search_dirs'
with mock.patch(search_dirs, lambda *args, **kwargs: []):
p = self.Path('/path/to/include')
self.assertEqual(self.compiler.flags(opts.option_list(
opts.include_dir(HeaderDirectory(p))
)), ['-I' + p])

self.assertEqual(self.compiler.flags(opts.option_list(
opts.include_dir(HeaderDirectory(p, system=True))
)), ['-isystem', p])
self.assertEqual(self.compiler.flags(opts.option_list(
opts.include_dir(HeaderDirectory(p, system=True))
)), ['-isystem', p])

if self.env.target_platform.genus == 'linux':
p = self.Path('/usr/include')
p = self.Path('/usr/include')
with mock.patch(search_dirs, lambda *args, **kwargs: [p]):
self.assertEqual(self.compiler.flags(opts.option_list(
opts.include_dir(HeaderDirectory(p, system=True))
)), ['-I' + p])
)), [])

def test_flags_define(self):
self.assertEqual(self.compiler.flags(opts.option_list(
Expand Down
17 changes: 16 additions & 1 deletion test/unit/tools/cc/test_linker.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from textwrap import dedent
from unittest import mock

from ... import *
Expand All @@ -9,7 +10,7 @@
from bfg9000.file_types import *
from bfg9000.tools.cc import CcBuilder
from bfg9000.packages import Framework
from bfg9000.path import InstallRoot, Path, Root
from bfg9000.path import abspath, InstallRoot, Path, Root


class TestCcLinker(CrossPlatformTestCase):
Expand All @@ -29,6 +30,8 @@ def _get_output_file(self):

def setUp(self):
self.linker = self._get_linker('c++')
self.linker.sysroot._reset(self.linker)
self.linker.search_dirs._reset(self.linker)

def test_call(self):
extra = self.linker._always_flags
Expand Down Expand Up @@ -68,6 +71,18 @@ def mock_execute(*args, **kwargs):
self.linker.sysroot(True)

def test_search_dirs(self):
def mock_execute(*args, **kwargs):
return dedent("""\
install: /usr/lib/gcc
libraries: =/usr/lib{}/path/to/lib
""".format(os.pathsep))

dirs = [abspath('/usr/lib'), abspath('/path/to/lib')]
with mock.patch('bfg9000.shell.execute', mock_execute):
self.assertEqual(self.linker.search_dirs(), dirs)
self.assertEqual(self.linker.search_dirs(True), dirs)

def test_search_dirs_broken(self):
def mock_execute(*args, **kwargs):
raise OSError()

Expand Down

0 comments on commit c727d0d

Please sign in to comment.