Skip to content

Commit b62e046

Browse files
DmT021marcprux
authored andcommitted
[SE-0480] Build settings for warning control flags (#8315)
This change adds warning control settings as described in the [proposal](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0480-swiftpm-warning-control.md). The flags for Swift targets are described by SE-0443, for Clang targets - [here](https://clang.llvm.org/docs/UsersManual.html#options-to-control-error-and-warning-messages).
1 parent 26c6e8e commit b62e046

File tree

13 files changed

+1168
-10
lines changed

13 files changed

+1168
-10
lines changed

Sources/Build/BuildDescription/ClangModuleBuildDescription.swift

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -363,11 +363,18 @@ public final class ClangModuleBuildDescription {
363363

364364
// suppress warnings if the package is remote
365365
if self.package.isRemote {
366-
args += ["-w"]
367-
// `-w` (suppress warnings) and `-Werror` (warnings as errors) flags are mutually exclusive
368-
if let index = args.firstIndex(of: "-Werror") {
369-
args.remove(at: index)
366+
// `-w` (suppress warnings) and the other warning control flags are mutually exclusive
367+
args = args.filter { arg in
368+
// we consider the following flags:
369+
// -Wxxxx
370+
// -Wno-xxxx
371+
// -Werror
372+
// -Werror=xxxx
373+
// -Wno-error
374+
// -Wno-error=xxxx
375+
arg.count <= 2 || !arg.starts(with: "-W")
370376
}
377+
args += ["-w"]
371378
}
372379

373380
return args

Sources/Build/BuildDescription/SwiftModuleBuildDescription.swift

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -638,11 +638,27 @@ public final class SwiftModuleBuildDescription {
638638

639639
// suppress warnings if the package is remote
640640
if self.package.isRemote {
641-
args += ["-suppress-warnings"]
642-
// suppress-warnings and warnings-as-errors are mutually exclusive
643-
if let index = args.firstIndex(of: "-warnings-as-errors") {
644-
args.remove(at: index)
641+
// suppress-warnings and the other warning control flags are mutually exclusive
642+
var removeNextArg = false
643+
args = args.filter { arg in
644+
if removeNextArg {
645+
removeNextArg = false
646+
return false
647+
}
648+
switch arg {
649+
case "-warnings-as-errors", "-no-warnings-as-errors":
650+
return false
651+
case "-Wwarning", "-Werror":
652+
removeNextArg = true
653+
return false
654+
default:
655+
return true
656+
}
645657
}
658+
guard !removeNextArg else {
659+
throw InternalError("Unexpected '-Wwarning' or '-Werror' at the end of args")
660+
}
661+
args += ["-suppress-warnings"]
646662
}
647663

648664
// Pass `-user-module-version` for versioned packages that aren't pre-releases.

Sources/PackageDescription/BuildSettings.swift

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,92 @@ public struct CSetting: Sendable {
203203
public static func unsafeFlags(_ flags: [String], _ condition: BuildSettingCondition? = nil) -> CSetting {
204204
return CSetting(name: "unsafeFlags", value: flags, condition: condition)
205205
}
206+
207+
/// Controls how all C compiler warnings are treated during compilation.
208+
///
209+
/// Use this setting to specify whether all warnings should be treated as warnings (default behavior)
210+
/// or as errors. This is equivalent to passing `-Werror` or `-Wno-error`
211+
/// to the C compiler.
212+
///
213+
/// This setting applies to all warnings emitted by the C compiler. To control specific
214+
/// warnings individually, use `treatWarning(name:as:_:)` instead.
215+
///
216+
/// - Since: First available in PackageDescription 6.2.
217+
///
218+
/// - Parameters:
219+
/// - level: The treatment level for all warnings (`.warning` or `.error`).
220+
/// - condition: A condition that restricts the application of the build setting.
221+
@available(_PackageDescription, introduced: 6.2)
222+
public static func treatAllWarnings(
223+
as level: WarningLevel,
224+
_ condition: BuildSettingCondition? = nil
225+
) -> CSetting {
226+
return CSetting(
227+
name: "treatAllWarnings", value: [level.rawValue], condition: condition)
228+
}
229+
230+
/// Controls how a specific C compiler warning is treated during compilation.
231+
///
232+
/// Use this setting to specify whether a particular warning should be treated as a warning
233+
/// (default behavior) or as an error. This is equivalent to passing `-Werror=` or `-Wno-error=`
234+
/// followed by the warning name to the C compiler.
235+
///
236+
/// This setting allows for fine-grained control over individual warnings. To control all
237+
/// warnings at once, use `treatAllWarnings(as:_:)` instead.
238+
///
239+
/// - Since: First available in PackageDescription 6.2.
240+
///
241+
/// - Parameters:
242+
/// - name: The name of the specific warning to control.
243+
/// - level: The treatment level for the warning (`.warning` or `.error`).
244+
/// - condition: A condition that restricts the application of the build setting.
245+
@available(_PackageDescription, introduced: 6.2)
246+
public static func treatWarning(
247+
_ name: String,
248+
as level: WarningLevel,
249+
_ condition: BuildSettingCondition? = nil
250+
) -> CSetting {
251+
return CSetting(
252+
name: "treatWarning", value: [name, level.rawValue], condition: condition)
253+
}
254+
255+
/// Enable a specific C compiler warning group.
256+
///
257+
/// Use this setting to enable a specific warning group. This is equivalent to passing
258+
/// `-W` followed by the group name to the C compiler.
259+
///
260+
/// - Since: First available in PackageDescription 6.2.
261+
///
262+
/// - Parameters:
263+
/// - name: The name of the warning group to enable.
264+
/// - condition: A condition that restricts the application of the build setting.
265+
@available(_PackageDescription, introduced: 6.2)
266+
public static func enableWarning(
267+
_ name: String,
268+
_ condition: BuildSettingCondition? = nil
269+
) -> CSetting {
270+
return CSetting(
271+
name: "enableWarning", value: [name], condition: condition)
272+
}
273+
274+
/// Disable a specific C compiler warning group.
275+
///
276+
/// Use this setting to disable a specific warning group. This is equivalent to passing
277+
/// `-Wno-` followed by the group name to the C compiler.
278+
///
279+
/// - Since: First available in PackageDescription 6.2.
280+
///
281+
/// - Parameters:
282+
/// - name: The name of the warning group to disable.
283+
/// - condition: A condition that restricts the application of the build setting.
284+
@available(_PackageDescription, introduced: 6.2)
285+
public static func disableWarning(
286+
_ name: String,
287+
_ condition: BuildSettingCondition? = nil
288+
) -> CSetting {
289+
return CSetting(
290+
name: "disableWarning", value: [name], condition: condition)
291+
}
206292
}
207293

208294
/// A CXX-language build setting.
@@ -273,6 +359,92 @@ public struct CXXSetting: Sendable {
273359
public static func unsafeFlags(_ flags: [String], _ condition: BuildSettingCondition? = nil) -> CXXSetting {
274360
return CXXSetting(name: "unsafeFlags", value: flags, condition: condition)
275361
}
362+
363+
/// Controls how all C++ compiler warnings are treated during compilation.
364+
///
365+
/// Use this setting to specify whether all warnings should be treated as warnings (default behavior)
366+
/// or as errors. This is equivalent to passing `-Werror` or `-Wno-error`
367+
/// to the C++ compiler.
368+
///
369+
/// This setting applies to all warnings emitted by the C++ compiler. To control specific
370+
/// warnings individually, use `treatWarning(name:as:_:)` instead.
371+
///
372+
/// - Since: First available in PackageDescription 6.2.
373+
///
374+
/// - Parameters:
375+
/// - level: The treatment level for all warnings (`.warning` or `.error`).
376+
/// - condition: A condition that restricts the application of the build setting.
377+
@available(_PackageDescription, introduced: 6.2)
378+
public static func treatAllWarnings(
379+
as level: WarningLevel,
380+
_ condition: BuildSettingCondition? = nil
381+
) -> CXXSetting {
382+
return CXXSetting(
383+
name: "treatAllWarnings", value: [level.rawValue], condition: condition)
384+
}
385+
386+
/// Controls how a specific C++ compiler warning is treated during compilation.
387+
///
388+
/// Use this setting to specify whether a particular warning should be treated as a warning
389+
/// (default behavior) or as an error. This is equivalent to passing `-Werror=` or `-Wno-error=`
390+
/// followed by the warning name to the C++ compiler.
391+
///
392+
/// This setting allows for fine-grained control over individual warnings. To control all
393+
/// warnings at once, use `treatAllWarnings(as:_:)` instead.
394+
///
395+
/// - Since: First available in PackageDescription 6.2.
396+
///
397+
/// - Parameters:
398+
/// - name: The name of the specific warning to control.
399+
/// - level: The treatment level for the warning (`.warning` or `.error`).
400+
/// - condition: A condition that restricts the application of the build setting.
401+
@available(_PackageDescription, introduced: 6.2)
402+
public static func treatWarning(
403+
_ name: String,
404+
as level: WarningLevel,
405+
_ condition: BuildSettingCondition? = nil
406+
) -> CXXSetting {
407+
return CXXSetting(
408+
name: "treatWarning", value: [name, level.rawValue], condition: condition)
409+
}
410+
411+
/// Enable a specific C++ compiler warning group.
412+
///
413+
/// Use this setting to enable a specific warning group. This is equivalent to passing
414+
/// `-W` followed by the group name to the C++ compiler.
415+
///
416+
/// - Since: First available in PackageDescription 6.2.
417+
///
418+
/// - Parameters:
419+
/// - name: The name of the warning group to enable.
420+
/// - condition: A condition that restricts the application of the build setting.
421+
@available(_PackageDescription, introduced: 6.2)
422+
public static func enableWarning(
423+
_ name: String,
424+
_ condition: BuildSettingCondition? = nil
425+
) -> CXXSetting {
426+
return CXXSetting(
427+
name: "enableWarning", value: [name], condition: condition)
428+
}
429+
430+
/// Disable a specific C++ compiler warning group.
431+
///
432+
/// Use this setting to disable a specific warning group. This is equivalent to passing
433+
/// `-Wno-` followed by the group name to the C++ compiler.
434+
///
435+
/// - Since: First available in PackageDescription 6.2.
436+
///
437+
/// - Parameters:
438+
/// - name: The name of the warning group to disable.
439+
/// - condition: A condition that restricts the application of the build setting.
440+
@available(_PackageDescription, introduced: 6.2)
441+
public static func disableWarning(
442+
_ name: String,
443+
_ condition: BuildSettingCondition? = nil
444+
) -> CXXSetting {
445+
return CXXSetting(
446+
name: "disableWarning", value: [name], condition: condition)
447+
}
276448
}
277449

278450
/// A Swift language build setting.
@@ -465,6 +637,54 @@ public struct SwiftSetting: Sendable {
465637
name: "swiftLanguageMode", value: [.init(describing: mode)], condition: condition)
466638
}
467639

640+
/// Controls how all Swift compiler warnings are treated during compilation.
641+
///
642+
/// Use this setting to specify whether all warnings should be treated as warnings (default behavior)
643+
/// or as errors. This is equivalent to passing `-warnings-as-errors` or `-no-warnings-as-errors`
644+
/// to the Swift compiler.
645+
///
646+
/// This setting applies to all warnings emitted by the Swift compiler. To control specific
647+
/// warnings individually, use `treatWarning(name:as:_:)` instead.
648+
///
649+
/// - Since: First available in PackageDescription 6.2.
650+
///
651+
/// - Parameters:
652+
/// - level: The treatment level for all warnings (`.warning` or `.error`).
653+
/// - condition: A condition that restricts the application of the build setting.
654+
@available(_PackageDescription, introduced: 6.2)
655+
public static func treatAllWarnings(
656+
as level: WarningLevel,
657+
_ condition: BuildSettingCondition? = nil
658+
) -> SwiftSetting {
659+
return SwiftSetting(
660+
name: "treatAllWarnings", value: [level.rawValue], condition: condition)
661+
}
662+
663+
/// Controls how a specific Swift compiler warning is treated during compilation.
664+
///
665+
/// Use this setting to specify whether a particular warning should be treated as a warning
666+
/// (default behavior) or as an error. This is equivalent to passing `-Werror` or `-Wwarning`
667+
/// followed by the warning name to the Swift compiler.
668+
///
669+
/// This setting allows for fine-grained control over individual warnings. To control all
670+
/// warnings at once, use `treatAllWarnings(as:_:)` instead.
671+
///
672+
/// - Since: First available in PackageDescription 6.2.
673+
///
674+
/// - Parameters:
675+
/// - name: The name of the specific warning to control.
676+
/// - level: The treatment level for the warning (`.warning` or `.error`).
677+
/// - condition: A condition that restricts the application of the build setting.
678+
@available(_PackageDescription, introduced: 6.2)
679+
public static func treatWarning(
680+
_ name: String,
681+
as level: WarningLevel,
682+
_ condition: BuildSettingCondition? = nil
683+
) -> SwiftSetting {
684+
return SwiftSetting(
685+
name: "treatWarning", value: [name, level.rawValue], condition: condition)
686+
}
687+
468688
/// Set the default isolation to the given global actor type.
469689
///
470690
/// - Since: First available in PackageDescription 6.2.

Sources/PackageDescription/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ add_library(PackageDescription
2323
Target.swift
2424
Trait.swift
2525
Version.swift
26-
Version+StringLiteralConvertible.swift)
26+
Version+StringLiteralConvertible.swift
27+
WarningLevel.swift)
2728

2829
target_compile_options(PackageDescription PUBLIC
2930
$<$<COMPILE_LANGUAGE:Swift>:-package-description-version$<SEMICOLON>999.0>)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//
2+
// This source file is part of the Swift open source project
3+
//
4+
// Copyright (c) 2025 Apple Inc. and the Swift project authors
5+
// Licensed under Apache License v2.0 with Runtime Library Exception
6+
//
7+
// See http://swift.org/LICENSE.txt for license information
8+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
9+
//
10+
//===----------------------------------------------------------------------===//
11+
12+
/// The level at which a compiler warning should be treated.
13+
///
14+
/// This enum is used with the `SwiftSetting.treatAllWarnings(as:_:)` and
15+
/// `SwiftSetting.treatWarning(name:as:_:)` methods to control how warnings
16+
/// are handled during compilation.
17+
@available(_PackageDescription, introduced: 6.2)
18+
public enum WarningLevel: String {
19+
/// Treat as a warning.
20+
///
21+
/// Warnings will be displayed during compilation but will not cause the build to fail.
22+
case warning
23+
24+
/// Treat as an error.
25+
///
26+
/// Warnings will be elevated to errors, causing the build to fail if any such warnings occur.
27+
case error
28+
}

Sources/PackageLoading/ManifestJSONParser.swift

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,45 @@ extension TargetBuildSettingDescription.Kind {
749749
}
750750

751751
return .swiftLanguageMode(version)
752+
case "treatAllWarnings":
753+
guard values.count == 1 else {
754+
throw InternalError("invalid build settings value")
755+
}
756+
757+
let rawLevel = values[0]
758+
759+
guard let level = TargetBuildSettingDescription.WarningLevel(rawValue: rawLevel) else {
760+
throw InternalError("unknown warning treat level: \(rawLevel)")
761+
}
762+
763+
return .treatAllWarnings(level)
764+
765+
case "treatWarning":
766+
guard values.count == 2 else {
767+
throw InternalError("invalid build settings value")
768+
}
769+
770+
let name = values[0]
771+
let rawValue = values[1]
772+
773+
guard let level = TargetBuildSettingDescription.WarningLevel(rawValue: rawValue) else {
774+
throw InternalError("unknown warning treat level: \(rawValue)")
775+
}
776+
777+
return .treatWarning(name, level)
778+
779+
case "enableWarning":
780+
guard values.count == 1 else {
781+
throw InternalError("invalid build settings value")
782+
}
783+
return .enableWarning(values[0])
784+
785+
case "disableWarning":
786+
guard values.count == 1 else {
787+
throw InternalError("invalid build settings value")
788+
}
789+
return .disableWarning(values[0])
790+
752791
case "defaultIsolation":
753792
guard let rawValue = values.first else {
754793
throw InternalError("invalid (empty) build settings value")

0 commit comments

Comments
 (0)