diff --git a/Sources/Build/BuildDescription/ClangModuleBuildDescription.swift b/Sources/Build/BuildDescription/ClangModuleBuildDescription.swift index 3aadda6b52b..4116e970b14 100644 --- a/Sources/Build/BuildDescription/ClangModuleBuildDescription.swift +++ b/Sources/Build/BuildDescription/ClangModuleBuildDescription.swift @@ -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 diff --git a/Sources/Build/BuildDescription/SwiftModuleBuildDescription.swift b/Sources/Build/BuildDescription/SwiftModuleBuildDescription.swift index fc9dc8c317a..5c71b64a66d 100644 --- a/Sources/Build/BuildDescription/SwiftModuleBuildDescription.swift +++ b/Sources/Build/BuildDescription/SwiftModuleBuildDescription.swift @@ -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. diff --git a/Sources/PackageDescription/BuildSettings.swift b/Sources/PackageDescription/BuildSettings.swift index c008b3f100f..55e495a7391 100644 --- a/Sources/PackageDescription/BuildSettings.swift +++ b/Sources/PackageDescription/BuildSettings.swift @@ -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. @@ -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. @@ -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. diff --git a/Sources/PackageDescription/CMakeLists.txt b/Sources/PackageDescription/CMakeLists.txt index 757d2ddfde0..9d417490d55 100644 --- a/Sources/PackageDescription/CMakeLists.txt +++ b/Sources/PackageDescription/CMakeLists.txt @@ -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 $<$:-package-description-version$999.0>) diff --git a/Sources/PackageDescription/WarningLevel.swift b/Sources/PackageDescription/WarningLevel.swift new file mode 100644 index 00000000000..1d4a953fc32 --- /dev/null +++ b/Sources/PackageDescription/WarningLevel.swift @@ -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 +} diff --git a/Sources/PackageLoading/ManifestJSONParser.swift b/Sources/PackageLoading/ManifestJSONParser.swift index 1082aaf2a6e..07ca0e6eacc 100644 --- a/Sources/PackageLoading/ManifestJSONParser.swift +++ b/Sources/PackageLoading/ManifestJSONParser.swift @@ -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") diff --git a/Sources/PackageLoading/PackageBuilder.swift b/Sources/PackageLoading/PackageBuilder.swift index 5e8f0c81122..376aec70585 100644 --- a/Sources/PackageLoading/PackageBuilder.swift +++ b/Sources/PackageLoading/PackageBuilder.swift @@ -1253,6 +1253,96 @@ public final class PackageBuilder { values = [version.rawValue] + case .treatAllWarnings(let level): + switch setting.tool { + case .c: + decl = .OTHER_CFLAGS + let flag = switch level { + case .error: "-Werror" + case .warning: "-Wno-error" + } + values = [flag] + + case .cxx: + decl = .OTHER_CPLUSPLUSFLAGS + let flag = switch level { + case .error: "-Werror" + case .warning: "-Wno-error" + } + values = [flag] + + case .linker: + throw InternalError("linker does not support treatAllWarnings") + + case .swift: + // We can't use SWIFT_WARNINGS_AS_WARNINGS_GROUPS and + // SWIFT_WARNINGS_AS_ERRORS_GROUPS here. + // See https://github.com/swiftlang/swift-build/issues/248 + decl = .OTHER_SWIFT_FLAGS + let flag = switch level { + case .error: "-warnings-as-errors" + case .warning: "-no-warnings-as-errors" + } + values = [flag] + } + + case .treatWarning(let name, let level): + switch setting.tool { + case .c: + decl = .OTHER_CFLAGS + let flag = switch level { + case .error: "-Werror=\(name)" + case .warning: "-Wno-error=\(name)" + } + values = [flag] + + case .cxx: + decl = .OTHER_CPLUSPLUSFLAGS + let flag = switch level { + case .error: "-Werror=\(name)" + case .warning: "-Wno-error=\(name)" + } + values = [flag] + + case .linker: + throw InternalError("linker does not support treatWarning") + + case .swift: + // We can't use SWIFT_WARNINGS_AS_WARNINGS_GROUPS and + // SWIFT_WARNINGS_AS_ERRORS_GROUPS here. + // See https://github.com/swiftlang/swift-build/issues/248 + decl = .OTHER_SWIFT_FLAGS + let flag = switch level { + case .error: "-Werror" + case .warning: "-Wwarning" + } + values = [flag, name] + } + + case .enableWarning(let name): + switch setting.tool { + case .c: + decl = .OTHER_CFLAGS + values = ["-W\(name)"] + case .cxx: + decl = .OTHER_CPLUSPLUSFLAGS + values = ["-W\(name)"] + case .swift, .linker: + throw InternalError("enableWarning is supported by C/C++") + } + + case .disableWarning(let name): + switch setting.tool { + case .c: + decl = .OTHER_CFLAGS + values = ["-Wno-\(name)"] + case .cxx: + decl = .OTHER_CPLUSPLUSFLAGS + values = ["-Wno-\(name)"] + case .swift, .linker: + throw InternalError("disableWarning is supported by C/C++") + } + case .defaultIsolation(let isolation): switch setting.tool { case .c, .cxx, .linker: diff --git a/Sources/PackageModel/Manifest/TargetBuildSettingDescription.swift b/Sources/PackageModel/Manifest/TargetBuildSettingDescription.swift index 4bf0b71ae1b..559a8d2ecec 100644 --- a/Sources/PackageModel/Manifest/TargetBuildSettingDescription.swift +++ b/Sources/PackageModel/Manifest/TargetBuildSettingDescription.swift @@ -25,6 +25,12 @@ public enum TargetBuildSettingDescription { case Cxx } + /// The level at which a compiler warning should be treated. + public enum WarningLevel: String, Codable, Hashable, Sendable { + case warning + case error + } + public enum DefaultIsolation: String, Codable, Hashable, Sendable { case MainActor case nonisolated @@ -47,6 +53,11 @@ public enum TargetBuildSettingDescription { case swiftLanguageMode(SwiftLanguageVersion) + case treatAllWarnings(WarningLevel) + case treatWarning(String, WarningLevel) + case enableWarning(String) + case disableWarning(String) + case defaultIsolation(DefaultIsolation) public var isUnsafeFlags: Bool { @@ -56,7 +67,7 @@ public enum TargetBuildSettingDescription { return !flags.isEmpty case .headerSearchPath, .define, .linkedLibrary, .linkedFramework, .interoperabilityMode, .enableUpcomingFeature, .enableExperimentalFeature, .strictMemorySafety, .swiftLanguageMode, - .defaultIsolation: + .treatAllWarnings, .treatWarning, .enableWarning, .disableWarning, .defaultIsolation: return false } } diff --git a/Sources/PackageModel/ManifestSourceGeneration.swift b/Sources/PackageModel/ManifestSourceGeneration.swift index 98bd54b5313..23873f79158 100644 --- a/Sources/PackageModel/ManifestSourceGeneration.swift +++ b/Sources/PackageModel/ManifestSourceGeneration.swift @@ -573,6 +573,31 @@ fileprivate extension SourceCodeFragment { params.append(SourceCodeFragment(from: condition)) } self.init(enum: setting.kind.name, subnodes: params) + case .treatAllWarnings(let level): + params.append(SourceCodeFragment(key: "as", enum: level.rawValue)) + if let condition = setting.condition { + params.append(SourceCodeFragment(from: condition)) + } + self.init(enum: setting.kind.name, subnodes: params) + case .treatWarning(let name, let level): + params.append(SourceCodeFragment(string: name)) + params.append(SourceCodeFragment(key: "as", enum: level.rawValue)) + if let condition = setting.condition { + params.append(SourceCodeFragment(from: condition)) + } + self.init(enum: setting.kind.name, subnodes: params) + case .enableWarning(let name): + params.append(SourceCodeFragment(string: name)) + if let condition = setting.condition { + params.append(SourceCodeFragment(from: condition)) + } + self.init(enum: setting.kind.name, subnodes: params) + case .disableWarning(let name): + params.append(SourceCodeFragment(string: name)) + if let condition = setting.condition { + params.append(SourceCodeFragment(from: condition)) + } + self.init(enum: setting.kind.name, subnodes: params) case .defaultIsolation(let isolation): switch isolation { case .MainActor: @@ -1021,6 +1046,14 @@ extension TargetBuildSettingDescription.Kind { return "strictMemorySafety" case .swiftLanguageMode: return "swiftLanguageMode" + case .treatAllWarnings: + return "treatAllWarnings" + case .treatWarning: + return "treatWarning" + case .enableWarning: + return "enableWarning" + case .disableWarning: + return "disableWarning" case .defaultIsolation: return "defaultIsolation" } diff --git a/Tests/BuildTests/BuildPlanTests.swift b/Tests/BuildTests/BuildPlanTests.swift index 7e2d9348212..1d6a2693a5b 100644 --- a/Tests/BuildTests/BuildPlanTests.swift +++ b/Tests/BuildTests/BuildPlanTests.swift @@ -29,6 +29,7 @@ import SPMBuildCore import _InternalBuildTestSupport import _InternalTestSupport import SwiftDriver +import TSCTestSupport import Workspace import XCTest @@ -4644,6 +4645,353 @@ class BuildPlanTestCase: BuildSystemProviderTestCase { } } + func testWarningLevelSettings() async throws { + let Pkg: AbsolutePath = "/Pkg" + + let fs: FileSystem = InMemoryFileSystem( + emptyFiles: + Pkg.appending(components: "Sources", "swiftLib", "lib.swift").pathString, + Pkg.appending(components: "Sources", "cLib", "lib.c").pathString, + Pkg.appending(components: "Sources", "cLib", "include", "lib.h").pathString, + Pkg.appending(components: "Sources", "cxxLib", "lib.cpp").pathString, + Pkg.appending(components: "Sources", "cxxLib", "include", "lib.h").pathString + ) + + let observability = ObservabilitySystem.makeForTesting() + let graph = try loadModulesGraph( + fileSystem: fs, + manifests: [ + Manifest.createRootManifest( + displayName: "Pkg", + path: .init(validating: Pkg.pathString), + toolsVersion: .v6_2, + targets: [ + TargetDescription( + name: "swiftLib", + dependencies: [], + settings: [ + .init(tool: .swift, kind: .treatAllWarnings(.warning), condition: .init(config: "debug")), + .init(tool: .swift, kind: .treatAllWarnings(.error), condition: .init(config: "release")), + .init(tool: .swift, kind: .treatWarning("DeprecatedDeclaration", .warning), condition: .init(config: "debug")), + .init(tool: .swift, kind: .treatWarning("DeprecatedDeclaration", .error), condition: .init(config: "release")), + ] + ), + TargetDescription( + name: "cLib", + dependencies: [], + settings: [ + .init(tool: .c, kind: .treatAllWarnings(.warning), condition: .init(config: "debug")), + .init(tool: .c, kind: .treatAllWarnings(.error), condition: .init(config: "release")), + .init(tool: .c, kind: .treatWarning("deprecated-declarations", .warning), condition: .init(config: "debug")), + .init(tool: .c, kind: .treatWarning("deprecated-declarations", .error), condition: .init(config: "release")), + ] + ), + TargetDescription( + name: "cxxLib", + dependencies: [], + settings: [ + .init(tool: .cxx, kind: .treatAllWarnings(.warning), condition: .init(config: "debug")), + .init(tool: .cxx, kind: .treatAllWarnings(.error), condition: .init(config: "release")), + .init(tool: .cxx, kind: .treatWarning("deprecated-declarations", .warning), condition: .init(config: "debug")), + .init(tool: .cxx, kind: .treatWarning("deprecated-declarations", .error), condition: .init(config: "release")), + ] + ) + ] + ), + ], + observabilityScope: observability.topScope + ) + XCTAssertNoDiagnostics(observability.diagnostics) + + // Test debug configuration + do { + let result = try await BuildPlanResult(plan: mockBuildPlan( + environment: BuildEnvironment(platform: .macOS, configuration: .debug), + graph: graph, + fileSystem: fs, + observabilityScope: observability.topScope + )) + + // Check Swift warning treatment flags + let swiftLib = try result.moduleBuildDescription(for: "swiftLib").swift().compileArguments() + XCTAssertMatch(swiftLib, [.anySequence, "-no-warnings-as-errors", "-Wwarning", "DeprecatedDeclaration", .anySequence]) + + // Check C warning treatment flags + let cLib = try result.moduleBuildDescription(for: "cLib").clang().basicArguments(isCXX: false) + XCTAssertMatch(cLib, [.anySequence, "-Wno-error", "-Wno-error=deprecated-declarations", .anySequence]) + + // Check C++ warning treatment flags + let cxxLib = try result.moduleBuildDescription(for: "cxxLib").clang().basicArguments(isCXX: true) + XCTAssertMatch(cxxLib, [.anySequence, "-Wno-error", "-Wno-error=deprecated-declarations", .anySequence]) + } + + // Test release configuration + do { + let result = try await BuildPlanResult(plan: mockBuildPlan( + environment: BuildEnvironment(platform: .macOS, configuration: .release), + graph: graph, + fileSystem: fs, + observabilityScope: observability.topScope + )) + + // Check Swift warning treatment flags + let swiftLib = try result.moduleBuildDescription(for: "swiftLib").swift().compileArguments() + XCTAssertMatch(swiftLib, [.anySequence, "-warnings-as-errors", "-Werror", "DeprecatedDeclaration", .anySequence]) + + // Check C warning treatment flags + let cLib = try result.moduleBuildDescription(for: "cLib").clang().basicArguments(isCXX: false) + XCTAssertMatch(cLib, [.anySequence, "-Werror", "-Werror=deprecated-declarations", .anySequence]) + + // Check C++ warning treatment flags + let cxxLib = try result.moduleBuildDescription(for: "cxxLib").clang().basicArguments(isCXX: true) + XCTAssertMatch(cxxLib, [.anySequence, "-Werror", "-Werror=deprecated-declarations", .anySequence]) + } + } + + func testEnableDisableWarningSettings() async throws { + let Pkg: AbsolutePath = "/Pkg" + + let fs: FileSystem = InMemoryFileSystem( + emptyFiles: + Pkg.appending(components: "Sources", "cLib", "lib.c").pathString, + Pkg.appending(components: "Sources", "cLib", "include", "lib.h").pathString, + Pkg.appending(components: "Sources", "cxxLib", "lib.cpp").pathString, + Pkg.appending(components: "Sources", "cxxLib", "include", "lib.h").pathString + ) + + let observability = ObservabilitySystem.makeForTesting() + let graph = try loadModulesGraph( + fileSystem: fs, + manifests: [ + Manifest.createRootManifest( + displayName: "Pkg", + path: .init(validating: Pkg.pathString), + toolsVersion: .v6_2, + targets: [ + TargetDescription( + name: "cLib", + dependencies: [], + settings: [ + .init(tool: .c, kind: .enableWarning("implicit-fallthrough"), condition: .init(config: "debug")), + .init(tool: .c, kind: .disableWarning("unused-parameter"), condition: .init(config: "release")), + ] + ), + TargetDescription( + name: "cxxLib", + dependencies: [], + settings: [ + .init(tool: .cxx, kind: .enableWarning("implicit-fallthrough"), condition: .init(config: "debug")), + .init(tool: .cxx, kind: .disableWarning("unused-parameter"), condition: .init(config: "release")), + ] + ) + ] + ), + ], + observabilityScope: observability.topScope + ) + XCTAssertNoDiagnostics(observability.diagnostics) + + // Test debug configuration + do { + let result = try await BuildPlanResult(plan: mockBuildPlan( + environment: BuildEnvironment(platform: .macOS, configuration: .debug), + graph: graph, + fileSystem: fs, + observabilityScope: observability.topScope + )) + + // Check C flags + let cLib = try result.moduleBuildDescription(for: "cLib").clang().basicArguments(isCXX: false) + XCTAssertMatch(cLib, [.anySequence, "-Wimplicit-fallthrough", .anySequence]) + + // Check C++ flags + let cxxLib = try result.moduleBuildDescription(for: "cxxLib").clang().basicArguments(isCXX: true) + XCTAssertMatch(cxxLib, [.anySequence, "-Wimplicit-fallthrough", .anySequence]) + } + + // Test release configuration + do { + let result = try await BuildPlanResult(plan: mockBuildPlan( + environment: BuildEnvironment(platform: .macOS, configuration: .release), + graph: graph, + fileSystem: fs, + observabilityScope: observability.topScope + )) + + // Check C flags + let cLib = try result.moduleBuildDescription(for: "cLib").clang().basicArguments(isCXX: false) + XCTAssertMatch(cLib, [.anySequence, "-Wno-unused-parameter", .anySequence]) + + // Check C++ flags + let cxxLib = try result.moduleBuildDescription(for: "cxxLib").clang().basicArguments(isCXX: true) + XCTAssertMatch(cxxLib, [.anySequence, "-Wno-unused-parameter", .anySequence]) + } + } + + func testWarningSettingsInRemotePackage() async throws { + let Pkg: AbsolutePath = "/Pkg" + let RootPkg: AbsolutePath = "/RootPkg" + + let fs: FileSystem = InMemoryFileSystem( + emptyFiles: + RootPkg.appending(components: "Sources", "swiftTarget", "target.swift").pathString, + Pkg.appending(components: "Sources", "swiftLib", "lib.swift").pathString, + Pkg.appending(components: "Sources", "cLib", "lib.c").pathString, + Pkg.appending(components: "Sources", "cLib", "include", "lib.h").pathString, + Pkg.appending(components: "Sources", "cxxLib", "lib.cpp").pathString, + Pkg.appending(components: "Sources", "cxxLib", "include", "lib.h").pathString + ) + + let observability = ObservabilitySystem.makeForTesting() + let graph = try loadModulesGraph( + fileSystem: fs, + manifests: [ + Manifest.createRootManifest( + displayName: "RootPkg", + path: .init(validating: RootPkg.pathString), + toolsVersion: .v6_2, + dependencies: [ + .remoteSourceControl(url: "https://example.com/ext/pkg", requirement: .upToNextMajor(from: "1.0.0")), + ], + targets: [ + TargetDescription( + name: "swiftTarget", + dependencies: [ + .product(name: "swiftLib", package: "pkg"), + .product(name: "cLib", package: "pkg"), + .product(name: "cxxLib", package: "pkg"), + ] + ), + ] + ), + Manifest.createRemoteSourceControlManifest( + displayName: "Pkg", + url: "https://example.com/ext/pkg", + path: .init(validating: Pkg.pathString), + toolsVersion: .v6_2, + products: [ + ProductDescription(name: "swiftLib", type: .library(.static), targets: ["swiftLib"]), + ProductDescription(name: "cLib", type: .library(.static), targets: ["cLib"]), + ProductDescription(name: "cxxLib", type: .library(.static), targets: ["cxxLib"]), + ], + targets: [ + TargetDescription( + name: "swiftLib", + settings: [ + .init(tool: .swift, kind: .treatAllWarnings(.warning), condition: .init(config: "debug")), + .init(tool: .swift, kind: .treatAllWarnings(.error), condition: .init(config: "release")), + .init(tool: .swift, kind: .treatWarning("DeprecatedDeclaration", .warning), condition: .init(config: "debug")), + .init(tool: .swift, kind: .treatWarning("DeprecatedDeclaration", .error), condition: .init(config: "release")), + ] + ), + TargetDescription( + name: "cLib", + settings: [ + .init(tool: .c, kind: .enableWarning("implicit-fallthrough"), condition: .init(config: "debug")), + .init(tool: .c, kind: .disableWarning("unused-parameter"), condition: .init(config: "release")), + .init(tool: .c, kind: .treatAllWarnings(.warning), condition: .init(config: "debug")), + .init(tool: .c, kind: .treatAllWarnings(.error), condition: .init(config: "release")), + .init(tool: .c, kind: .treatWarning("deprecated-declarations", .warning), condition: .init(config: "debug")), + .init(tool: .c, kind: .treatWarning("deprecated-declarations", .error), condition: .init(config: "release")), + ] + ), + TargetDescription( + name: "cxxLib", + settings: [ + .init(tool: .cxx, kind: .enableWarning("implicit-fallthrough"), condition: .init(config: "debug")), + .init(tool: .cxx, kind: .disableWarning("unused-parameter"), condition: .init(config: "release")), + .init(tool: .cxx, kind: .treatAllWarnings(.warning), condition: .init(config: "debug")), + .init(tool: .cxx, kind: .treatAllWarnings(.error), condition: .init(config: "release")), + .init(tool: .cxx, kind: .treatWarning("deprecated-declarations", .warning), condition: .init(config: "debug")), + .init(tool: .cxx, kind: .treatWarning("deprecated-declarations", .error), condition: .init(config: "release")), + ] + ) + ] + ), + ], + observabilityScope: observability.topScope + ) + XCTAssertNoDiagnostics(observability.diagnostics) + + let swiftWarningControlFlags: [StringPattern] = [ + "DeprecatedDeclaration", + "-no-warnings-as-errors", + "-warnings-as-errors", + "-Wwarning", + "-Werror", + ] + let clangWarningControlFlags: [StringPattern] = [ + "-Wno-error", + "-Werror", + .prefix("-Wno-error="), + .prefix("-Werror="), + .prefix("-W"), + .prefix("-Wno-"), + ] + + // Test debug configuration + do { + let result = try await BuildPlanResult(plan: mockBuildPlan( + environment: BuildEnvironment(platform: .macOS, configuration: .debug), + graph: graph, + fileSystem: fs, + observabilityScope: observability.topScope + )) + + // Check Swift warning treatment flags + let swiftLib = try result.moduleBuildDescription(for: "swiftLib").swift().compileArguments() + for pattern in swiftWarningControlFlags { + XCTAssertNoMatch(swiftLib, [.anySequence, pattern, .anySequence]) + } + XCTAssertMatch(swiftLib, [.anySequence, "-suppress-warnings", .anySequence]) + + // Check C warning treatment flags + let cLib = try result.moduleBuildDescription(for: "cLib").clang().basicArguments(isCXX: false) + for pattern in clangWarningControlFlags { + XCTAssertNoMatch(cLib, [.anySequence, pattern, .anySequence]) + } + XCTAssertMatch(cLib, [.anySequence, "-w", .anySequence]) + + // Check C++ warning treatment flags + let cxxLib = try result.moduleBuildDescription(for: "cxxLib").clang().basicArguments(isCXX: true) + for pattern in clangWarningControlFlags { + XCTAssertNoMatch(cxxLib, [.anySequence, pattern, .anySequence]) + } + XCTAssertMatch(cxxLib, [.anySequence, "-w", .anySequence]) + } + + // Test release configuration + do { + let result = try await BuildPlanResult(plan: mockBuildPlan( + environment: BuildEnvironment(platform: .macOS, configuration: .release), + graph: graph, + fileSystem: fs, + observabilityScope: observability.topScope + )) + + // Check Swift warning treatment flags + let swiftLib = try result.moduleBuildDescription(for: "swiftLib").swift().compileArguments() + for pattern in swiftWarningControlFlags { + XCTAssertNoMatch(swiftLib, [.anySequence, pattern, .anySequence]) + } + XCTAssertMatch(swiftLib, [.anySequence, "-suppress-warnings", .anySequence]) + + // Check C warning treatment flags + let cLib = try result.moduleBuildDescription(for: "cLib").clang().basicArguments(isCXX: false) + for pattern in clangWarningControlFlags { + XCTAssertNoMatch(cLib, [.anySequence, pattern, .anySequence]) + } + XCTAssertMatch(cLib, [.anySequence, "-w", .anySequence]) + + // Check C++ warning treatment flags + let cxxLib = try result.moduleBuildDescription(for: "cxxLib").clang().basicArguments(isCXX: true) + for pattern in clangWarningControlFlags { + XCTAssertNoMatch(cxxLib, [.anySequence, pattern, .anySequence]) + } + XCTAssertMatch(cxxLib, [.anySequence, "-w", .anySequence]) + } + } + func testExtraBuildFlags() async throws { let fs = InMemoryFileSystem( emptyFiles: diff --git a/Tests/PackageLoadingTests/PD_6_2_LoadingTests.swift b/Tests/PackageLoadingTests/PD_6_2_LoadingTests.swift new file mode 100644 index 00000000000..25eb648284f --- /dev/null +++ b/Tests/PackageLoadingTests/PD_6_2_LoadingTests.swift @@ -0,0 +1,80 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +import Basics +import PackageModel +import SourceControl +import _InternalTestSupport +import XCTest + +final class PackageDescription6_2LoadingTests: PackageDescriptionLoadingTests { + override var toolsVersion: ToolsVersion { + .v6_2 + } + + func testWarningTreatingRules() async throws { + let content = """ + import PackageDescription + let package = Package( + name: "Foo", + products: [], + targets: [ + .target( + name: "Foo", + cSettings: [ + .enableWarning("implicit-fallthrough"), + .disableWarning("unused-parameter"), + .treatAllWarnings(as: .error), + .treatWarning("deprecated-declarations", as: .warning), + ], + cxxSettings: [ + .enableWarning("implicit-fallthrough"), + .disableWarning("unused-parameter"), + .treatAllWarnings(as: .warning), + .treatWarning("deprecated-declarations", as: .error), + ], + swiftSettings: [ + .treatAllWarnings(as: .error), + .treatWarning("DeprecatedDeclaration", as: .warning), + ] + ), + .target( + name: "Bar", + cSettings: [ + .enableWarning("implicit-fallthrough"), + .disableWarning("unused-parameter"), + .treatAllWarnings(as: .warning), + .treatWarning("deprecated-declarations", as: .error), + ], + cxxSettings: [ + .enableWarning("implicit-fallthrough"), + .disableWarning("unused-parameter"), + .treatAllWarnings(as: .error), + .treatWarning("deprecated-declarations", as: .warning), + ], + swiftSettings: [ + .treatAllWarnings(as: .warning), + .treatWarning("DeprecatedDeclaration", as: .error), + ] + ) + ] + ) + """ + + let observability = ObservabilitySystem.makeForTesting() + let (_, validationDiagnostics) = try await loadAndValidateManifest(content, observabilityScope: observability.topScope) + XCTAssertNoDiagnostics(validationDiagnostics) + testDiagnostics(observability.diagnostics) { result in + result.checkIsEmpty() + } + } +} diff --git a/Tests/PackageLoadingTests/PackageBuilderTests.swift b/Tests/PackageLoadingTests/PackageBuilderTests.swift index 7015fd08494..e0122b9caa7 100644 --- a/Tests/PackageLoadingTests/PackageBuilderTests.swift +++ b/Tests/PackageLoadingTests/PackageBuilderTests.swift @@ -3186,6 +3186,226 @@ final class PackageBuilderTests: XCTestCase { } } + func testSwiftWarningTreatingRules() throws { + let fs = InMemoryFileSystem(emptyFiles: + "/Sources/foo/foo.swift" + ) + + let manifest = Manifest.createRootManifest( + displayName: "pkg", + toolsVersion: .v6_2, + targets: [ + try TargetDescription( + name: "foo", + settings: [ + .init(tool: .swift, kind: .treatAllWarnings(.error), condition: .init(config: "release")), + .init(tool: .swift, kind: .treatAllWarnings(.warning), condition: .init(config: "debug")), + .init(tool: .swift, kind: .treatWarning("DeprecatedDeclaration", .error), condition: .init(config: "release")), + .init(tool: .swift, kind: .treatWarning("DeprecatedDeclaration", .warning), condition: .init(config: "debug")), + ] + ), + ] + ) + + PackageBuilderTester(manifest, in: fs) { package, _ in + package.checkModule("foo") { package in + let macosDebugScope = BuildSettings.Scope( + package.target.buildSettings, + environment: BuildEnvironment(platform: .macOS, configuration: .debug) + ) + XCTAssertEqual( + macosDebugScope.evaluate(.OTHER_SWIFT_FLAGS), + ["-no-warnings-as-errors", "-Wwarning", "DeprecatedDeclaration"] + ) + + let macosReleaseScope = BuildSettings.Scope( + package.target.buildSettings, + environment: BuildEnvironment(platform: .macOS, configuration: .release) + ) + XCTAssertEqual( + macosReleaseScope.evaluate(.OTHER_SWIFT_FLAGS), + ["-warnings-as-errors", "-Werror", "DeprecatedDeclaration"] + ) + } + } + } + + func testCWarningTreatingRules() throws { + let fs = InMemoryFileSystem(emptyFiles: + "/Sources/cfoo/foo.c", + "/Sources/cfoo/include/cfoo.h" + ) + + let manifest = Manifest.createRootManifest( + displayName: "pkg", + toolsVersion: .v6_2, + targets: [ + try TargetDescription( + name: "cfoo", + settings: [ + .init(tool: .c, kind: .treatAllWarnings(.error), condition: .init(config: "release")), + .init(tool: .c, kind: .treatAllWarnings(.warning), condition: .init(config: "debug")), + .init(tool: .c, kind: .treatWarning("deprecated-declarations", .error), condition: .init(config: "release")), + .init(tool: .c, kind: .treatWarning("deprecated-declarations", .warning), condition: .init(config: "debug")), + ] + ) + ] + ) + + PackageBuilderTester(manifest, in: fs) { package, _ in + package.checkModule("cfoo") { package in + let macosDebugScope = BuildSettings.Scope( + package.target.buildSettings, + environment: BuildEnvironment(platform: .macOS, configuration: .debug) + ) + XCTAssertEqual( + macosDebugScope.evaluate(.OTHER_CFLAGS), + ["-Wno-error", "-Wno-error=deprecated-declarations"] + ) + + let macosReleaseScope = BuildSettings.Scope( + package.target.buildSettings, + environment: BuildEnvironment(platform: .macOS, configuration: .release) + ) + XCTAssertEqual( + macosReleaseScope.evaluate(.OTHER_CFLAGS), + ["-Werror", "-Werror=deprecated-declarations"] + ) + } + } + } + + func testCXXWarningTreatingRules() throws { + let fs = InMemoryFileSystem(emptyFiles: + "/Sources/cxxfoo/foo.cpp", + "/Sources/cxxfoo/include/cxxfoo.h" + ) + + let manifest = Manifest.createRootManifest( + displayName: "pkg", + toolsVersion: .v6_2, + targets: [ + try TargetDescription( + name: "cxxfoo", + settings: [ + .init(tool: .cxx, kind: .treatAllWarnings(.error), condition: .init(config: "release")), + .init(tool: .cxx, kind: .treatAllWarnings(.warning), condition: .init(config: "debug")), + .init(tool: .cxx, kind: .treatWarning("deprecated-declarations", .error), condition: .init(config: "release")), + .init(tool: .cxx, kind: .treatWarning("deprecated-declarations", .warning), condition: .init(config: "debug")), + ] + ), + ] + ) + + PackageBuilderTester(manifest, in: fs) { package, _ in + package.checkModule("cxxfoo") { package in + let macosDebugScope = BuildSettings.Scope( + package.target.buildSettings, + environment: BuildEnvironment(platform: .macOS, configuration: .debug) + ) + XCTAssertEqual( + macosDebugScope.evaluate(.OTHER_CPLUSPLUSFLAGS), + ["-Wno-error", "-Wno-error=deprecated-declarations"] + ) + + let macosReleaseScope = BuildSettings.Scope( + package.target.buildSettings, + environment: BuildEnvironment(platform: .macOS, configuration: .release) + ) + XCTAssertEqual( + macosReleaseScope.evaluate(.OTHER_CPLUSPLUSFLAGS), + ["-Werror", "-Werror=deprecated-declarations"] + ) + } + } + } + + func testCWarningEnableDisable() throws { + let fs = InMemoryFileSystem(emptyFiles: + "/Sources/cfoo/foo.c", + "/Sources/cfoo/include/cfoo.h" + ) + + let manifest = Manifest.createRootManifest( + displayName: "pkg", + toolsVersion: .v6_2, + targets: [ + try TargetDescription( + name: "cfoo", + settings: [ + .init(tool: .c, kind: .enableWarning("implicit-fallthrough"), condition: .init(config: "debug")), + .init(tool: .c, kind: .disableWarning("unused-parameter"), condition: .init(config: "release")), + ] + ) + ] + ) + + PackageBuilderTester(manifest, in: fs) { package, _ in + package.checkModule("cfoo") { package in + let macosDebugScope = BuildSettings.Scope( + package.target.buildSettings, + environment: BuildEnvironment(platform: .macOS, configuration: .debug) + ) + XCTAssertEqual( + macosDebugScope.evaluate(.OTHER_CFLAGS), + ["-Wimplicit-fallthrough"] + ) + + let macosReleaseScope = BuildSettings.Scope( + package.target.buildSettings, + environment: BuildEnvironment(platform: .macOS, configuration: .release) + ) + XCTAssertEqual( + macosReleaseScope.evaluate(.OTHER_CFLAGS), + ["-Wno-unused-parameter"] + ) + } + } + } + + func testCXXWarningEnableDisable() throws { + let fs = InMemoryFileSystem(emptyFiles: + "/Sources/cxxfoo/foo.cpp", + "/Sources/cxxfoo/include/cxxfoo.h" + ) + + let manifest = Manifest.createRootManifest( + displayName: "pkg", + toolsVersion: .v6_2, + targets: [ + try TargetDescription( + name: "cxxfoo", + settings: [ + .init(tool: .cxx, kind: .enableWarning("implicit-fallthrough"), condition: .init(config: "debug")), + .init(tool: .cxx, kind: .disableWarning("unused-parameter"), condition: .init(config: "release")), + ] + ) + ] + ) + + PackageBuilderTester(manifest, in: fs) { package, _ in + package.checkModule("cxxfoo") { package in + let macosDebugScope = BuildSettings.Scope( + package.target.buildSettings, + environment: BuildEnvironment(platform: .macOS, configuration: .debug) + ) + XCTAssertEqual( + macosDebugScope.evaluate(.OTHER_CPLUSPLUSFLAGS), + ["-Wimplicit-fallthrough"] + ) + + let macosReleaseScope = BuildSettings.Scope( + package.target.buildSettings, + environment: BuildEnvironment(platform: .macOS, configuration: .release) + ) + XCTAssertEqual( + macosReleaseScope.evaluate(.OTHER_CPLUSPLUSFLAGS), + ["-Wno-unused-parameter"] + ) + } + } + } + func testDefaultIsolationPerTarget() throws { let fs = InMemoryFileSystem(emptyFiles: "/Sources/A/a.swift", diff --git a/Tests/WorkspaceTests/ManifestSourceGenerationTests.swift b/Tests/WorkspaceTests/ManifestSourceGenerationTests.swift index 3de3fc95699..75028cbdafe 100644 --- a/Tests/WorkspaceTests/ManifestSourceGenerationTests.swift +++ b/Tests/WorkspaceTests/ManifestSourceGenerationTests.swift @@ -859,6 +859,49 @@ final class ManifestSourceGenerationTests: XCTestCase { try await testManifestWritingRoundTrip(manifestContents: contents, toolsVersion: .v6_0) } + func testManifestGenerationWithWarningTreatingRules() async throws { + let manifest = Manifest.createRootManifest( + displayName: "pkg", + path: "/pkg", + toolsVersion: .v6_2, + dependencies: [], + targets: [ + try TargetDescription( + name: "swiftTarget", + settings: [ + .init(tool: .swift, kind: .treatAllWarnings(.error), condition: .init(config: "release")), + .init(tool: .swift, kind: .treatAllWarnings(.warning), condition: .init(config: "debug")), + .init(tool: .swift, kind: .treatWarning("DeprecatedDeclaration", .warning), condition: .init(config: "release")), + .init(tool: .swift, kind: .treatWarning("DeprecatedDeclaration", .error), condition: .init(config: "debug")), + ] + ), + try TargetDescription( + name: "cTarget", + settings: [ + .init(tool: .c, kind: .disableWarning("unused-parameter"), condition: .init(config: "release")), + .init(tool: .c, kind: .enableWarning("implicit-fallthrough"), condition: .init(config: "debug")), + .init(tool: .c, kind: .treatAllWarnings(.error), condition: .init(config: "release")), + .init(tool: .c, kind: .treatAllWarnings(.warning), condition: .init(config: "debug")), + .init(tool: .c, kind: .treatWarning("implicit-function-declaration", .error), condition: .init(config: "release")), + .init(tool: .c, kind: .treatWarning("implicit-function-declaration", .warning), condition: .init(config: "debug")), + ] + ), + try TargetDescription( + name: "cxxTarget", + settings: [ + .init(tool: .cxx, kind: .disableWarning("unused-parameter"), condition: .init(config: "release")), + .init(tool: .cxx, kind: .enableWarning("implicit-fallthrough"), condition: .init(config: "debug")), + .init(tool: .cxx, kind: .treatAllWarnings(.error), condition: .init(config: "release")), + .init(tool: .cxx, kind: .treatAllWarnings(.warning), condition: .init(config: "debug")), + .init(tool: .cxx, kind: .treatWarning("deprecated-declarations", .error), condition: .init(config: "release")), + .init(tool: .cxx, kind: .treatWarning("deprecated-declarations", .warning), condition: .init(config: "debug")), + ] + ), + ]) + let contents = try manifest.generateManifestFileContents(packageDirectory: manifest.path.parentDirectory) + try await testManifestWritingRoundTrip(manifestContents: contents, toolsVersion: .v6_2) + } + func testDefaultIsolation() async throws { let manifest = Manifest.createRootManifest( displayName: "pkg",