/* ======================================================================== * Copyright (c) 2005-2025 The OPC Foundation, Inc. All rights reserved. * * OPC Foundation MIT License 1.00 * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * The complete license agreement can be found here: * http://opcfoundation.org/License/MIT/1.00/ * ======================================================================*/ using System.Collections.Generic; using Microsoft.CodeAnalysis.Diagnostics; namespace Opc.Ua.SourceGeneration { /// /// Model compiler options /// internal sealed record class ModelCompilationOptions { /// /// Model options /// public DesignFileOptions Options { get; set; } /// /// -useAllowSubtypes /// public bool UseAllowSubtypes { get; set; } /// /// -exclude [id;id;id] /// public IReadOnlyList Exclude { get; set; } /// /// Opt out of . By /// default the source generator emits a typed asynchronous /// client wrapper ({TypeName}Client) for every /// ObjectType in the model — including types without /// methods, so downstream models can derive from the emitted /// proxies. Each generated proxy inherits from the proxy of its /// parent ObjectType (forming a chain that mirrors the OPC UA /// type hierarchy and ultimately roots at the hand-authored /// Opc.Ua.ObjectTypeClient base). Methods that share a /// name with an ancestor method are emitted with the C# /// new modifier so the derived signature shadows the /// ancestor. Set this to true to suppress proxy /// emission. Surfaced from MSBuild via the /// ModelSourceGeneratorOmitObjectTypeProxies property. /// public bool OmitObjectTypeProxies { get; set; } /// /// Optional override for the C# namespace used by classes emitted /// by the . By default /// proxies are emitted into the model's own namespace (i.e. the /// C# prefix of the model's target namespace) — for example the /// standard UA NodeSet emits into Opc.Ua. Set this option /// to redirect proxy emission into a different namespace. /// Surfaced from MSBuild via the /// ModelSourceGeneratorObjectTypeProxyNamespace property. /// public string ObjectTypeProxyNamespace { get; set; } /// /// When set to true, the generator uses the modelling rules /// from the referenced type definition unconditionally for all /// structural code generation decisions and the emitted runtime /// ModellingRuleId. When off (default), the generator /// enforces OPC UA modelling rule promotion semantics — /// instances may only promote, never demote, the type /// definition's rule. /// Surfaced from MSBuild via the /// ModelSourceGeneratorUseTypeDefinitionModellingRules property. /// public bool UseTypeDefinitionModellingRules { get; set; } /// /// When set to true, the predefined nodes generated for a /// NodeSet2 input are exactly the nodes of the NodeSet2. See /// GeneratorOptions.EmitNodeSetNodesOnly. /// Surfaced from MSBuild via the /// ModelSourceGeneratorEmitNodeSetNodesOnly property. /// public bool EmitNodeSetNodesOnly { get; set; } /// /// Override for whether the generator emits the /// [assembly: ModelDependencyAttribute] + corresponding /// [assembly: ModelSnapshotAttribute] metadata for the /// model(s) being generated. /// /// /// /// Auto (default): emit only when the consuming /// compilation produces a library /// ( / /// ). Applications never /// expose their model metadata to downstream consumers — they /// are leaf consumers themselves — so the attribute payload is /// silently skipped to save assembly bloat. /// /// /// Always: emit regardless of OutputKind. Useful for /// integration-test applications that simulate library /// behaviour. /// /// /// Never: do not emit, even from libraries. Useful when /// the consumer assembly is sealed and downstream usage is /// known impossible. /// /// /// Surfaced from MSBuild via the /// ModelSourceGeneratorEmitDependencyMetadata property. /// /// public EmitDependencyMetadataMode EmitDependencyMetadata { get; set; } = EmitDependencyMetadataMode.Auto; /// /// When set to true, suppresses emission of the /// per-ObjectType typed accessor extension classes /// ({TypeName}StateComponents + /// {TypeName}StateProperties) by the model generator. /// Defaults to false — accessors are emitted unless /// explicitly suppressed. Model-only libraries that don't /// reference Opc.Ua.Server must set this to true /// via the /// ModelSourceGeneratorOmitFluentApi MSBuild property, /// because the emitted accessor method bodies call into /// server-side fluent builders even though the /// Opc.Ua.IComponentAccessor<TState> / /// Opc.Ua.IPropertyAccessor<TState> marker /// interfaces themselves live in Opc.Ua.Types. /// public bool OmitFluentApi { get; set; } /// /// When set to true, a local model already supplied by a /// referenced assembly under the same model URI and C# prefix is /// loaded and validated, but only its per-ObjectType typed fluent /// accessor extension classes are emitted. The accessors reference /// the state types from the referenced model assembly. The referenced /// model metadata must explicitly declare that its producer omitted /// fluent accessors. /// Surfaced from MSBuild via the /// ModelSourceGeneratorFluentAccessorsOnly property. /// public bool FluentAccessorsOnly { get; set; } /// /// Opts into client accessor-only generation over a referenced model's existing proxies. /// public bool ClientAccessorsOnly { get; set; } /// /// Overrides the namespace of the generated client accessor extensions. /// public string ClientAccessorNamespace { get; set; } /// /// Overrides the generated partial extension class name. /// public string ClientAccessorClassName { get; set; } /// /// Suppresses event-record generation. /// public bool OmitEventRecords { get; set; } /// /// Get options from options provider /// public static ModelCompilationOptions From(AnalyzerConfigOptionsProvider provider) { return new ModelCompilationOptions { Options = new DesignFileOptions { Version = provider.GlobalOptions.GetString( nameof(DesignFileOptions.Version)) ?? "v105", StartId = (uint)provider.GlobalOptions.GetInteger( nameof(DesignFileOptions.StartId)), ModelVersion = provider.GlobalOptions.GetString( nameof(DesignFileOptions.ModelVersion)), ModelPublicationDate = provider.GlobalOptions.GetString( nameof(DesignFileOptions.ModelPublicationDate)), ReleaseCandidate = provider.GlobalOptions.GetBool( nameof(DesignFileOptions.ReleaseCandidate)), GenerateNodeManager = provider.GlobalOptions.GetBool( nameof(DesignFileOptions.GenerateNodeManager)) }, Exclude = provider.GlobalOptions.GetStrings(nameof(Exclude)), UseAllowSubtypes = provider.GlobalOptions.GetBool(nameof(UseAllowSubtypes)), OmitObjectTypeProxies = provider.GlobalOptions.GetBool( nameof(OmitObjectTypeProxies)), ObjectTypeProxyNamespace = provider.GlobalOptions.GetString( nameof(ObjectTypeProxyNamespace)), UseTypeDefinitionModellingRules = provider.GlobalOptions.GetBool( nameof(UseTypeDefinitionModellingRules)), EmitNodeSetNodesOnly = provider.GlobalOptions.GetBool( nameof(EmitNodeSetNodesOnly)), EmitDependencyMetadata = ParseEmitMode(provider.GlobalOptions.GetString( nameof(EmitDependencyMetadata))), OmitFluentApi = provider.GlobalOptions.GetBool( nameof(OmitFluentApi)), FluentAccessorsOnly = provider.GlobalOptions.GetBool( nameof(FluentAccessorsOnly)), ClientAccessorsOnly = provider.GlobalOptions.GetBool( nameof(ClientAccessorsOnly)), ClientAccessorNamespace = provider.GlobalOptions.GetString( nameof(ClientAccessorNamespace)), ClientAccessorClassName = provider.GlobalOptions.GetString( nameof(ClientAccessorClassName)), OmitEventRecords = provider.GlobalOptions.GetBool( nameof(OmitEventRecords)) }; } private static EmitDependencyMetadataMode ParseEmitMode(string raw) { if (string.IsNullOrWhiteSpace(raw)) { return EmitDependencyMetadataMode.Auto; } return raw.Trim().ToLowerInvariant() switch { "always" or "true" => EmitDependencyMetadataMode.Always, "never" or "false" => EmitDependencyMetadataMode.Never, _ => EmitDependencyMetadataMode.Auto }; } } /// /// Whether the generator emits cross-assembly model-dependency /// metadata attributes (ModelDependencyAttribute + /// ModelSnapshotAttribute). /// internal enum EmitDependencyMetadataMode { /// /// Emit only when building a library. /// Auto, /// /// Always emit (override). /// Always, /// /// Never emit (override). /// Never } }