Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add core:warnings_as_errors configuration option #15149

Merged
merged 9 commits into from
Dec 18, 2023
16 changes: 13 additions & 3 deletions conan/api/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class ConanOutput:
# Singleton
_conan_output_level = LEVEL_STATUS
_silent_warn_tags = []
_warnings_as_errors = False

def __init__(self, scope=""):
self.stream = sys.stderr
Expand All @@ -63,6 +64,10 @@ def __init__(self, scope=""):
def define_silence_warnings(cls, warnings):
cls._silent_warn_tags = warnings or []

@classmethod
def set_warnings_as_errors(cls, value):
cls._warnings_as_errors = value

@classmethod
def define_log_level(cls, v):
"""
Expand Down Expand Up @@ -204,11 +209,16 @@ def success(self, msg):
return self

def warning(self, msg, warn_tag=None):
if self._conan_output_level <= LEVEL_WARNING:
if warn_tag is not None and warn_tag in self._silent_warn_tags:
def _skip_warning():
return (warn_tag is not None and warn_tag in self._silent_warn_tags
or warn_tag is None and "unknown" in self._silent_warn_tags)
if self._conan_output_level <= LEVEL_WARNING or (self._warnings_as_errors and self._conan_output_level <= LEVEL_ERROR):
if _skip_warning():
return self
warn_tag_msg = "" if warn_tag is None else f"{warn_tag}: "
self._write_message(f"WARN: {warn_tag_msg}{msg}", Color.YELLOW)
message_type = "ERROR" if self._warnings_as_errors else "WARN"
message_color = Color.RED if self._warnings_as_errors else Color.YELLOW
self._write_message(f"{message_type}: {warn_tag_msg}{msg}", message_color)
AbrilRBS marked this conversation as resolved.
Show resolved Hide resolved
return self

def error(self, msg):
Expand Down
6 changes: 5 additions & 1 deletion conan/internal/conan_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,12 @@ def __init__(self, cache_folder, global_conf):

home_paths = HomePaths(self.cache_folder)
self.hook_manager = HookManager(home_paths.hooks_path)
# Wraps an http_requester to inject proxies, certs, etc

ConanOutput.set_warnings_as_errors(global_conf.get("core:warnings_as_errors",
default=False, check_type=bool))
AbrilRBS marked this conversation as resolved.
Show resolved Hide resolved
ConanOutput.define_silence_warnings(global_conf.get("core:skip_warnings", check_type=list))

# Wraps an http_requester to inject proxies, certs, etc
self.requester = ConanRequester(global_conf, cache_folder)
# To handle remote connections
rest_client_factory = RestApiClientFactory(self.requester, global_conf)
Expand Down
1 change: 1 addition & 0 deletions conans/model/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
BUILT_IN_CONFS = {
"core:required_conan_version": "Raise if current version does not match the defined range.",
"core:non_interactive": "Disable interactive user input, raises error if input necessary",
"core:warnings_as_errors": "Treat warnings as errors",
"core:skip_warnings": "Do not show warnings in this list",
"core:default_profile": "Defines the default host profile ('default' by default)",
"core:default_build_profile": "Defines the default build profile ('default' by default)",
Expand Down
197 changes: 197 additions & 0 deletions conans/test/integration/command_v2/test_output.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
import json
import textwrap

from conans.test.assets.genconanfile import GenConanfile
from conans.test.utils.tools import TestClient


class TestOutputLevel:
def test_invalid_output_level(self):
t = TestClient()
t.save({"conanfile.py": GenConanfile("foo", "1.0")})
t.run("create . -vfooling", assert_error=True)
assert "Invalid argument '-vfooling'"

def test_output_level(self):
lines = ("self.output.trace('This is a trace')",
"self.output.debug('This is a debug')",
"self.output.verbose('This is a verbose')",
"self.output.info('This is a info')",
"self.output.highlight('This is a highlight')",
"self.output.success('This is a success')",
"self.output.warning('This is a warning')",
"self.output.error('This is a error')",
)

t = TestClient()
t.save({"conanfile.py": GenConanfile("foo", "1.0").with_package(*lines)})

# By default, it prints > info
t.run("create .")
assert "This is a trace" not in t.out
assert "This is a debug" not in t.out
assert "This is a verbose" not in t.out
assert "This is a info" in t.out
assert "This is a highlight" in t.out
assert "This is a success" in t.out
assert "This is a warning" in t.out
assert "This is a error" in t.out

# Check if -v argument is equal to VERBOSE level
t.run("create . -v")
assert "This is a trace" not in t.out
assert "This is a debug" not in t.out
assert "This is a verbose" in t.out
assert "This is a info" in t.out
assert "This is a highlight" in t.out
assert "This is a success" in t.out
assert "This is a warning" in t.out
assert "This is a error" in t.out

# Print also verbose traces
t.run("create . -vverbose")
assert "This is a trace" not in t.out
assert "This is a debug" not in t.out
assert "This is a verbose" in t.out
assert "This is a info" in t.out
assert "This is a highlight" in t.out
assert "This is a success" in t.out
assert "This is a warning" in t.out
assert "This is a error" in t.out

# Print also debug traces
t.run("create . -vv")
assert "This is a trace" not in t.out
assert "This is a debug" in t.out
assert "This is a verbose" in t.out
assert "This is a info" in t.out
assert "This is a highlight" in t.out
assert "This is a success" in t.out
assert "This is a warning" in t.out
assert "This is a error" in t.out
t.run("create . -vdebug")
assert "This is a trace" not in t.out
assert "This is a debug" in t.out
assert "This is a verbose" in t.out
assert "This is a info" in t.out
assert "This is a highlight" in t.out
assert "This is a success" in t.out
assert "This is a warning" in t.out
assert "This is a error" in t.out

# Print also "trace" traces
t.run("create . -vvv")
assert "This is a trace" in t.out
assert "This is a debug" in t.out
assert "This is a verbose" in t.out
assert "This is a info" in t.out
assert "This is a highlight" in t.out
assert "This is a success" in t.out
assert "This is a warning" in t.out
assert "This is a error" in t.out
t.run("create . -vtrace")
assert "This is a trace" in t.out
assert "This is a debug" in t.out
assert "This is a verbose" in t.out
assert "This is a info" in t.out
assert "This is a highlight" in t.out
assert "This is a success" in t.out
assert "This is a warning" in t.out
assert "This is a error" in t.out

# With notice
t.run("create . -vstatus")
assert "This is a trace" not in t.out
assert "This is a debug" not in t.out
assert "This is a verbose" not in t.out
assert "This is a info" in t.out
assert "This is a highlight" in t.out
assert "This is a success" in t.out
assert "This is a warning" in t.out
assert "This is a error" in t.out

# With notice
t.run("create . -vnotice")
assert "This is a trace" not in t.out
assert "This is a debug" not in t.out
assert "This is a verbose" not in t.out
assert "This is a info" not in t.out
assert "This is a highlight" in t.out
assert "This is a success" in t.out
assert "This is a warning" in t.out
assert "This is a error" in t.out

# With warnings
t.run("create . -vwarning")
assert "This is a trace" not in t.out
assert "This is a debug" not in t.out
assert "This is a verbose" not in t.out
assert "This is a info" not in t.out
assert "This is a highlight" not in t.out
assert "This is a success" not in t.out
assert "This is a warning" in t.out
assert "This is a error" in t.out

# With errors
t.run("create . -verror")
assert "This is a trace" not in t.out
assert "This is a debug" not in t.out
assert "This is a verbose" not in t.out
assert "This is a info" not in t.out
assert "This is a highlight" not in t.out
assert "This is a success" not in t.out
assert "This is a warning" not in t.out
assert "This is a error" in t.out


class TestWarningHandling:
lines = ("self.output.warning('Untagged warning')",
"self.output.warning('Tagged warning', warn_tag='tag')")

def test_warning_as_error(self):
t = TestClient()
t.save({"conanfile.py": GenConanfile("foo", "1.0").with_package(*self.lines)})

t.save_home({"global.conf": "core:warnings_as_errors=False"})
t.run("create . -vwarning")
assert "WARN: Untagged warning" in t.out
assert "WARN: tag: Tagged warning" in t.out

t.save_home({"global.conf": "core:warnings_as_errors=True"})
t.run("create . -vwarning")
assert "ERROR: Untagged warning" in t.out
assert "ERROR: tag: Tagged warning" in t.out

t.save_home({"global.conf": """core:warnings_as_errors=True\ncore:skip_warnings=["tag"]"""})
t.run("create . -verror")
assert "ERROR: Untagged warning" in t.out
assert "ERROR: tag: Tagged warning" not in t.out

t.save_home({"global.conf": "core:warnings_as_errors=False"})
t.run("create . -verror")
assert "ERROR: Untagged warning" not in t.out
assert "ERROR: tag: Tagged warning" not in t.out

def test_skip_warnings(self):
t = TestClient()
t.save({"conanfile.py": GenConanfile("foo", "1.0").with_package(*self.lines)})

t.save_home({"global.conf": "core:skip_warnings=[]"})
t.run("create . -vwarning")
assert "WARN: Untagged warning" in t.out
assert "WARN: tag: Tagged warning" in t.out

t.save_home({"global.conf": "core:skip_warnings=['tag']"})
t.run("create . -vwarning")
assert "WARN: Untagged warning" in t.out
assert "WARN: tag: Tagged warning" not in t.out

t.save_home({"global.conf": "core:skip_warnings=['unknown']"})
t.run("create . -vwarning")
assert "WARN: Untagged warning" not in t.out
assert "WARN: tag: Tagged warning" in t.out

t.save_home({"global.conf": "core:skip_warnings=['unknown', 'tag']"})
t.run("create . -vwarning")
assert "WARN: Untagged warning" not in t.out
assert "WARN: tag: Tagged warning" not in t.out
Loading