using System; using System.Collections.Generic; using System.IO; using System.Text; using Xamarin.Bundler; namespace Xamarin.Utils { public class CompilerFlags { public Application Application { get { return Target.App; } } public Target Target; public HashSet Frameworks; // if a file, "-F /path/to/X --framework X" and added to Inputs, otherwise "--framework X". public HashSet WeakFrameworks; public HashSet LinkWithLibraries; // X, added to Inputs public HashSet ForceLoadLibraries; // -force_load X, added to Inputs public HashSet OtherFlags; // X public HashSet Defines; // -DX public HashSet UnresolvedSymbols; // -u X public HashSet SourceFiles; // X, added to Inputs // Here we store a list of all the file-system based inputs // to the compiler. This is used when determining if the // compiler needs to be called in the first place (dependency // tracking). public List Inputs; public HashSet AllLibraries { get { var rv = new HashSet (); if (LinkWithLibraries != null) rv.UnionWith (LinkWithLibraries); if (ForceLoadLibraries != null) rv.UnionWith (ForceLoadLibraries); return rv; } } public void ReferenceSymbol (string symbol) { if (UnresolvedSymbols == null) UnresolvedSymbols = new HashSet (); UnresolvedSymbols.Add (symbol); } public void ReferenceSymbols (IEnumerable symbols) { if (UnresolvedSymbols == null) UnresolvedSymbols = new HashSet (); foreach (var symbol in symbols) UnresolvedSymbols.Add (symbol); } public void AddDefine (string define) { if (Defines == null) Defines = new HashSet (); Defines.Add (define); } public void AddLinkWith (string library, bool force_load = false) { if (LinkWithLibraries == null) LinkWithLibraries = new HashSet (); if (ForceLoadLibraries == null) ForceLoadLibraries = new HashSet (); if (force_load) { ForceLoadLibraries.Add (library); } else { LinkWithLibraries.Add (library); } } public void AddLinkWith (IEnumerable libraries, bool force_load = false) { if (libraries == null) return; foreach (var lib in libraries) AddLinkWith (lib, force_load); } public void AddSourceFile (string file) { if (SourceFiles == null) SourceFiles = new HashSet (); SourceFiles.Add (file); } public void AddOtherFlag (string flag) { if (OtherFlags == null) OtherFlags = new HashSet (); OtherFlags.Add (flag); } public void AddOtherFlags (IEnumerable flags) { if (flags == null) return; if (OtherFlags == null) OtherFlags = new HashSet (); OtherFlags.UnionWith (flags); } public void LinkWithMono () { var mode = Target.App.LibMonoLinkMode; switch (mode) { case AssemblyBuildTarget.DynamicLibrary: case AssemblyBuildTarget.StaticObject: AddLinkWith (Application.GetLibMono (mode)); break; case AssemblyBuildTarget.Framework: AddFramework (Application.GetLibMono (mode)); break; default: throw ErrorHelper.CreateError (100, "Invalid assembly build target: '{0}'. Please file a bug report with a test case (http://bugzilla.xamarin.com).", mode); } } public void LinkWithXamarin () { var mode = Target.App.LibXamarinLinkMode; switch (mode) { case AssemblyBuildTarget.DynamicLibrary: case AssemblyBuildTarget.StaticObject: AddLinkWith (Application.GetLibXamarin (mode)); break; case AssemblyBuildTarget.Framework: AddFramework (Application.GetLibXamarin (mode)); break; default: throw ErrorHelper.CreateError (100, "Invalid assembly build target: '{0}'. Please file a bug report with a test case (http://bugzilla.xamarin.com).", mode); } AddFramework ("Foundation"); AddOtherFlag ("-lz"); } public void LinkWithPInvokes (Abi abi) { if (!Application.FastDev || !Application.RequiresPInvokeWrappers) return; AddOtherFlag (Driver.Quote (Path.Combine (Application.Cache.Location, abi.AsArchString (), "libpinvokes.dylib"))); } public void AddFramework (string framework) { if (Frameworks == null) Frameworks = new HashSet (); Frameworks.Add (framework); } public void AddFrameworks (IEnumerable frameworks, IEnumerable weak_frameworks) { if (frameworks != null) { if (Frameworks == null) Frameworks = new HashSet (); Frameworks.UnionWith (frameworks); } if (weak_frameworks != null) { if (WeakFrameworks == null) WeakFrameworks = new HashSet (); WeakFrameworks.UnionWith (weak_frameworks); } } public void Prepare () { // Check for system frameworks that are only available in newer iOS versions, // (newer than the deployment target), in which case those need to be weakly linked. if (Frameworks != null) { if (WeakFrameworks == null) WeakFrameworks = new HashSet (); foreach (var fwk in Frameworks) { if (!fwk.EndsWith (".framework", StringComparison.Ordinal)) { var add_to = WeakFrameworks; var framework = Driver.GetFrameworks (Application).Find (fwk); if (framework != null) { if (framework.Version > Application.SdkVersion) continue; add_to = Application.DeploymentTarget >= framework.Version ? Frameworks : WeakFrameworks; } add_to.Add (fwk); } else { // believe what we got about user frameworks. } } // Make sure frameworks aren't duplicated, favoring any weak frameworks. Frameworks.ExceptWith (WeakFrameworks); } // force_load libraries take precedence, so remove the libraries // we need to force load from the list of libraries we just load. if (LinkWithLibraries != null) LinkWithLibraries.ExceptWith (ForceLoadLibraries); } void AddInput (string file) { if (Inputs == null) return; Inputs.Add (file); } public void WriteArguments (StringBuilder args) { Prepare (); ProcessFrameworksForArguments (args); if (LinkWithLibraries != null) { foreach (var lib in LinkWithLibraries) { args.Append (' ').Append (Driver.Quote (lib)); AddInput (lib); } } if (ForceLoadLibraries != null) { foreach (var lib in ForceLoadLibraries) { args.Append (" -force_load ").Append (Driver.Quote (lib)); AddInput (lib); } } if (OtherFlags != null) { foreach (var flag in OtherFlags) args.Append (' ').Append (flag); } if (Defines != null) { foreach (var define in Defines) args.Append (" -D").Append (define); } if (UnresolvedSymbols != null) { foreach (var symbol in UnresolvedSymbols) args.Append (" -u ").Append (Driver.Quote ("_" + symbol)); } if (SourceFiles != null) { foreach (var src in SourceFiles) { args.Append (' ').Append (Driver.Quote (src)); AddInput (src); } } } void ProcessFrameworksForArguments (StringBuilder args) { bool any_user_framework = false; if (Frameworks != null) { foreach (var fw in Frameworks) ProcessFrameworkForArguments (args, fw, false, ref any_user_framework); } if (WeakFrameworks != null) { foreach (var fw in WeakFrameworks) ProcessFrameworkForArguments (args, fw, true, ref any_user_framework); } if (any_user_framework) { args.Append (" -Xlinker -rpath -Xlinker @executable_path/Frameworks"); if (Application.IsExtension) args.Append (" -Xlinker -rpath -Xlinker @executable_path/../../Frameworks"); } if (Application.FastDev) args.Append (" -Xlinker -rpath -Xlinker @executable_path"); } void ProcessFrameworkForArguments (StringBuilder args, string fw, bool is_weak, ref bool any_user_framework) { var name = Path.GetFileNameWithoutExtension (fw); if (fw.EndsWith (".framework", StringComparison.Ordinal)) { // user framework, we need to pass -F to the linker so that the linker finds the user framework. any_user_framework = true; AddInput (Path.Combine (fw, name)); args.Append (" -F ").Append (Driver.Quote (Path.GetDirectoryName (fw))); } args.Append (is_weak ? " -weak_framework " : " -framework ").Append (name); } public override string ToString () { var args = new StringBuilder (); WriteArguments (args); return args.ToString (); } public void PopulateInputs () { var args = new StringBuilder (); Inputs = new List (); WriteArguments (args); } } }