Skip to content

Build settings for warning treating rules (SE-0443, draft) #8315

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -363,11 +363,18 @@ public final class ClangModuleBuildDescription {

// suppress warnings if the package is remote
if self.package.isRemote {
args += ["-w"]
// `-w` (suppress warnings) and `-Werror` (warnings as errors) flags are mutually exclusive
if let index = args.firstIndex(of: "-Werror") {
args.remove(at: index)
// `-w` (suppress warnings) and the other warning control flags are mutually exclusive
args = args.filter { arg in
// we consider the following flags:
// -Wxxxx
// -Wno-xxxx
// -Werror
// -Werror=xxxx
// -Wno-error
// -Wno-error=xxxx
arg.count <= 2 || !arg.starts(with: "-W")
}
args += ["-w"]
}

return args
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -621,11 +621,27 @@ public final class SwiftModuleBuildDescription {

// suppress warnings if the package is remote
if self.package.isRemote {
args += ["-suppress-warnings"]
// suppress-warnings and warnings-as-errors are mutually exclusive
if let index = args.firstIndex(of: "-warnings-as-errors") {
args.remove(at: index)
// suppress-warnings and the other warning control flags are mutually exclusive
var removeNextArg = false
args = args.filter { arg in
if removeNextArg {
removeNextArg = false
return false
}
switch arg {
case "-warnings-as-errors", "-no-warnings-as-errors":
return false
case "-Wwarning", "-Werror":
removeNextArg = true
return false
default:
return true
}
}
guard !removeNextArg else {
throw InternalError("Unexpected '-Wwarning' or '-Werror' at the end of args")
}
args += ["-suppress-warnings"]
}

// Pass `-user-module-version` for versioned packages that aren't pre-releases.
Expand Down
220 changes: 220 additions & 0 deletions Sources/PackageDescription/BuildSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,92 @@ public struct CSetting: Sendable {
public static func unsafeFlags(_ flags: [String], _ condition: BuildSettingCondition? = nil) -> CSetting {
return CSetting(name: "unsafeFlags", value: flags, condition: condition)
}

/// Controls how all C compiler warnings are treated during compilation.
///
/// Use this setting to specify whether all warnings should be treated as warnings (default behavior)
/// or as errors. This is equivalent to passing `-Werror` or `-Wno-error`
/// to the C compiler.
///
/// This setting applies to all warnings emitted by the C compiler. To control specific
/// warnings individually, use `treatWarning(name:as:_:)` instead.
///
/// - Since: First available in PackageDescription 6.2.
///
/// - Parameters:
/// - level: The treatment level for all warnings (`.warning` or `.error`).
/// - condition: A condition that restricts the application of the build setting.
@available(_PackageDescription, introduced: 6.2)
public static func treatAllWarnings(
as level: WarningLevel,
_ condition: BuildSettingCondition? = nil
) -> CSetting {
return CSetting(
name: "treatAllWarnings", value: [level.rawValue], condition: condition)
}

/// Controls how a specific C compiler warning is treated during compilation.
///
/// Use this setting to specify whether a particular warning should be treated as a warning
/// (default behavior) or as an error. This is equivalent to passing `-Werror=` or `-Wno-error=`
/// followed by the warning name to the C compiler.
///
/// This setting allows for fine-grained control over individual warnings. To control all
/// warnings at once, use `treatAllWarnings(as:_:)` instead.
///
/// - Since: First available in PackageDescription 6.2.
///
/// - Parameters:
/// - name: The name of the specific warning to control.
/// - level: The treatment level for the warning (`.warning` or `.error`).
/// - condition: A condition that restricts the application of the build setting.
@available(_PackageDescription, introduced: 6.2)
public static func treatWarning(
_ name: String,
as level: WarningLevel,
_ condition: BuildSettingCondition? = nil
) -> CSetting {
return CSetting(
name: "treatWarning", value: [name, level.rawValue], condition: condition)
}

/// Enable a specific C compiler warning group.
///
/// Use this setting to enable a specific warning group. This is equivalent to passing
/// `-W` followed by the group name to the C compiler.
///
/// - Since: First available in PackageDescription 6.2.
///
/// - Parameters:
/// - name: The name of the warning group to enable.
/// - condition: A condition that restricts the application of the build setting.
@available(_PackageDescription, introduced: 6.2)
public static func enableWarning(
_ name: String,
_ condition: BuildSettingCondition? = nil
) -> CSetting {
return CSetting(
name: "enableWarning", value: [name], condition: condition)
}

/// Disable a specific C compiler warning group.
///
/// Use this setting to disable a specific warning group. This is equivalent to passing
/// `-Wno-` followed by the group name to the C compiler.
///
/// - Since: First available in PackageDescription 6.2.
///
/// - Parameters:
/// - name: The name of the warning group to disable.
/// - condition: A condition that restricts the application of the build setting.
@available(_PackageDescription, introduced: 6.2)
public static func disableWarning(
_ name: String,
_ condition: BuildSettingCondition? = nil
) -> CSetting {
return CSetting(
name: "disableWarning", value: [name], condition: condition)
}
}

/// A CXX-language build setting.
Expand Down Expand Up @@ -271,6 +357,92 @@ public struct CXXSetting: Sendable {
public static func unsafeFlags(_ flags: [String], _ condition: BuildSettingCondition? = nil) -> CXXSetting {
return CXXSetting(name: "unsafeFlags", value: flags, condition: condition)
}

/// Controls how all C++ compiler warnings are treated during compilation.
///
/// Use this setting to specify whether all warnings should be treated as warnings (default behavior)
/// or as errors. This is equivalent to passing `-Werror` or `-Wno-error`
/// to the C++ compiler.
///
/// This setting applies to all warnings emitted by the C++ compiler. To control specific
/// warnings individually, use `treatWarning(name:as:_:)` instead.
///
/// - Since: First available in PackageDescription 6.2.
///
/// - Parameters:
/// - level: The treatment level for all warnings (`.warning` or `.error`).
/// - condition: A condition that restricts the application of the build setting.
@available(_PackageDescription, introduced: 6.2)
public static func treatAllWarnings(
as level: WarningLevel,
_ condition: BuildSettingCondition? = nil
) -> CXXSetting {
return CXXSetting(
name: "treatAllWarnings", value: [level.rawValue], condition: condition)
}

/// Controls how a specific C++ compiler warning is treated during compilation.
///
/// Use this setting to specify whether a particular warning should be treated as a warning
/// (default behavior) or as an error. This is equivalent to passing `-Werror=` or `-Wno-error=`
/// followed by the warning name to the C++ compiler.
///
/// This setting allows for fine-grained control over individual warnings. To control all
/// warnings at once, use `treatAllWarnings(as:_:)` instead.
///
/// - Since: First available in PackageDescription 6.2.
///
/// - Parameters:
/// - name: The name of the specific warning to control.
/// - level: The treatment level for the warning (`.warning` or `.error`).
/// - condition: A condition that restricts the application of the build setting.
@available(_PackageDescription, introduced: 6.2)
public static func treatWarning(
_ name: String,
as level: WarningLevel,
_ condition: BuildSettingCondition? = nil
) -> CXXSetting {
return CXXSetting(
name: "treatWarning", value: [name, level.rawValue], condition: condition)
}

/// Enable a specific C++ compiler warning group.
///
/// Use this setting to enable a specific warning group. This is equivalent to passing
/// `-W` followed by the group name to the C++ compiler.
///
/// - Since: First available in PackageDescription 6.2.
///
/// - Parameters:
/// - name: The name of the warning group to enable.
/// - condition: A condition that restricts the application of the build setting.
@available(_PackageDescription, introduced: 6.2)
public static func enableWarning(
_ name: String,
_ condition: BuildSettingCondition? = nil
) -> CXXSetting {
return CXXSetting(
name: "enableWarning", value: [name], condition: condition)
}

/// Disable a specific C++ compiler warning group.
///
/// Use this setting to disable a specific warning group. This is equivalent to passing
/// `-Wno-` followed by the group name to the C++ compiler.
///
/// - Since: First available in PackageDescription 6.2.
///
/// - Parameters:
/// - name: The name of the warning group to disable.
/// - condition: A condition that restricts the application of the build setting.
@available(_PackageDescription, introduced: 6.2)
public static func disableWarning(
_ name: String,
_ condition: BuildSettingCondition? = nil
) -> CXXSetting {
return CXXSetting(
name: "disableWarning", value: [name], condition: condition)
}
}

/// A Swift language build setting.
Expand Down Expand Up @@ -463,6 +635,54 @@ public struct SwiftSetting: Sendable {
name: "swiftLanguageMode", value: [.init(describing: mode)], condition: condition)
}

/// Controls how all Swift compiler warnings are treated during compilation.
///
/// Use this setting to specify whether all warnings should be treated as warnings (default behavior)
/// or as errors. This is equivalent to passing `-warnings-as-errors` or `-no-warnings-as-errors`
/// to the Swift compiler.
///
/// This setting applies to all warnings emitted by the Swift compiler. To control specific
/// warnings individually, use `treatWarning(name:as:_:)` instead.
///
/// - Since: First available in PackageDescription 6.2.
///
/// - Parameters:
/// - level: The treatment level for all warnings (`.warning` or `.error`).
/// - condition: A condition that restricts the application of the build setting.
@available(_PackageDescription, introduced: 6.2)
public static func treatAllWarnings(
as level: WarningLevel,
_ condition: BuildSettingCondition? = nil
) -> SwiftSetting {
return SwiftSetting(
name: "treatAllWarnings", value: [level.rawValue], condition: condition)
}

/// Controls how a specific Swift compiler warning is treated during compilation.
///
/// Use this setting to specify whether a particular warning should be treated as a warning
/// (default behavior) or as an error. This is equivalent to passing `-Werror` or `-Wwarning`
/// followed by the warning name to the Swift compiler.
///
/// This setting allows for fine-grained control over individual warnings. To control all
/// warnings at once, use `treatAllWarnings(as:_:)` instead.
///
/// - Since: First available in PackageDescription 6.2.
///
/// - Parameters:
/// - name: The name of the specific warning to control.
/// - level: The treatment level for the warning (`.warning` or `.error`).
/// - condition: A condition that restricts the application of the build setting.
@available(_PackageDescription, introduced: 6.2)
public static func treatWarning(
_ name: String,
as level: WarningLevel,
_ condition: BuildSettingCondition? = nil
) -> SwiftSetting {
return SwiftSetting(
name: "treatWarning", value: [name, level.rawValue], condition: condition)
}

/// Set the default isolation to the given global actor type.
///
/// - Since: First available in PackageDescription 6.2.
Expand Down
3 changes: 2 additions & 1 deletion Sources/PackageDescription/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ add_library(PackageDescription
Target.swift
Trait.swift
Version.swift
Version+StringLiteralConvertible.swift)
Version+StringLiteralConvertible.swift
WarningLevel.swift)

target_compile_options(PackageDescription PUBLIC
$<$<COMPILE_LANGUAGE:Swift>:-package-description-version$<SEMICOLON>999.0>)
Expand Down
28 changes: 28 additions & 0 deletions Sources/PackageDescription/WarningLevel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2025 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

/// The level at which a compiler warning should be treated.
///
/// This enum is used with the `SwiftSetting.treatAllWarnings(as:_:)` and
/// `SwiftSetting.treatWarning(name:as:_:)` methods to control how warnings
/// are handled during compilation.
@available(_PackageDescription, introduced: 6.2)
public enum WarningLevel: String {
/// Treat as a warning.
///
/// Warnings will be displayed during compilation but will not cause the build to fail.
case warning

/// Treat as an error.
///
/// Warnings will be elevated to errors, causing the build to fail if any such warnings occur.
case error
}
39 changes: 39 additions & 0 deletions Sources/PackageLoading/ManifestJSONParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,45 @@ extension TargetBuildSettingDescription.Kind {
}

return .swiftLanguageMode(version)
case "treatAllWarnings":
guard values.count == 1 else {
throw InternalError("invalid build settings value")
}

let rawLevel = values[0]

guard let level = TargetBuildSettingDescription.WarningLevel(rawValue: rawLevel) else {
throw InternalError("unknown warning treat level: \(rawLevel)")
}

return .treatAllWarnings(level)

case "treatWarning":
guard values.count == 2 else {
throw InternalError("invalid build settings value")
}

let name = values[0]
let rawValue = values[1]

guard let level = TargetBuildSettingDescription.WarningLevel(rawValue: rawValue) else {
throw InternalError("unknown warning treat level: \(rawValue)")
}

return .treatWarning(name, level)

case "enableWarning":
guard values.count == 1 else {
throw InternalError("invalid build settings value")
}
return .enableWarning(values[0])

case "disableWarning":
guard values.count == 1 else {
throw InternalError("invalid build settings value")
}
return .disableWarning(values[0])

case "defaultIsolation":
guard let rawValue = values.first else {
throw InternalError("invalid (empty) build settings value")
Expand Down
Loading