2016-10-14 13:34:35 +03:00
using System ;
2016-04-21 15:57:02 +03:00
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 < string > Frameworks ; // if a file, "-F /path/to/X --framework X" and added to Inputs, otherwise "--framework X".
public HashSet < string > WeakFrameworks ;
public HashSet < string > LinkWithLibraries ; // X, added to Inputs
public HashSet < string > ForceLoadLibraries ; // -force_load X, added to Inputs
public HashSet < string > OtherFlags ; // X
public HashSet < string > Defines ; // -DX
public HashSet < string > UnresolvedSymbols ; // -u X
2017-01-25 17:06:56 +03:00
public HashSet < string > SourceFiles ; // X, added to Inputs
2016-04-21 15:57:02 +03:00
// 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 < string > Inputs ;
2017-01-25 19:30:04 +03:00
public CompilerFlags ( Target target )
{
if ( target = = null )
throw new ArgumentNullException ( nameof ( target ) ) ;
this . Target = target ;
}
2017-01-25 15:07:37 +03:00
public HashSet < string > AllLibraries {
get {
var rv = new HashSet < string > ( ) ;
if ( LinkWithLibraries ! = null )
rv . UnionWith ( LinkWithLibraries ) ;
if ( ForceLoadLibraries ! = null )
rv . UnionWith ( ForceLoadLibraries ) ;
return rv ;
}
}
2016-04-21 15:57:02 +03:00
public void ReferenceSymbol ( string symbol )
{
if ( UnresolvedSymbols = = null )
UnresolvedSymbols = new HashSet < string > ( ) ;
UnresolvedSymbols . Add ( symbol ) ;
}
2017-01-18 12:22:53 +03:00
public void ReferenceSymbols ( IEnumerable < string > symbols )
{
if ( UnresolvedSymbols = = null )
UnresolvedSymbols = new HashSet < string > ( ) ;
foreach ( var symbol in symbols )
UnresolvedSymbols . Add ( symbol ) ;
}
2016-04-21 15:57:02 +03:00
public void AddDefine ( string define )
{
if ( Defines = = null )
Defines = new HashSet < string > ( ) ;
Defines . Add ( define ) ;
}
public void AddLinkWith ( string library , bool force_load = false )
{
if ( LinkWithLibraries = = null )
LinkWithLibraries = new HashSet < string > ( ) ;
if ( ForceLoadLibraries = = null )
ForceLoadLibraries = new HashSet < string > ( ) ;
if ( force_load ) {
ForceLoadLibraries . Add ( library ) ;
} else {
LinkWithLibraries . Add ( library ) ;
}
}
public void AddLinkWith ( IEnumerable < string > libraries , bool force_load = false )
{
if ( libraries = = null )
return ;
foreach ( var lib in libraries )
AddLinkWith ( lib , force_load ) ;
}
2017-01-25 17:06:56 +03:00
public void AddSourceFile ( string file )
{
if ( SourceFiles = = null )
SourceFiles = new HashSet < string > ( ) ;
SourceFiles . Add ( file ) ;
}
2016-04-21 15:57:02 +03:00
public void AddOtherFlag ( string flag )
{
if ( OtherFlags = = null )
OtherFlags = new HashSet < string > ( ) ;
OtherFlags . Add ( flag ) ;
}
public void AddOtherFlags ( IEnumerable < string > flags )
{
if ( flags = = null )
return ;
if ( OtherFlags = = null )
OtherFlags = new HashSet < string > ( ) ;
OtherFlags . UnionWith ( flags ) ;
}
public void LinkWithMono ( )
{
2017-01-24 21:08:15 +03:00
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 ) ;
2016-04-21 15:57:02 +03:00
}
}
public void LinkWithXamarin ( )
{
2017-01-24 21:08:15 +03:00
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 ) ;
}
2016-04-21 15:57:02 +03:00
AddFramework ( "Foundation" ) ;
AddOtherFlag ( "-lz" ) ;
}
public void AddFramework ( string framework )
{
if ( Frameworks = = null )
Frameworks = new HashSet < string > ( ) ;
Frameworks . Add ( framework ) ;
}
public void AddFrameworks ( IEnumerable < string > frameworks , IEnumerable < string > weak_frameworks )
{
if ( frameworks ! = null ) {
if ( Frameworks = = null )
Frameworks = new HashSet < string > ( ) ;
Frameworks . UnionWith ( frameworks ) ;
}
if ( weak_frameworks ! = null ) {
if ( WeakFrameworks = = null )
WeakFrameworks = new HashSet < string > ( ) ;
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 < string > ( ) ;
foreach ( var fwk in Frameworks ) {
2016-10-14 13:34:35 +03:00
if ( ! fwk . EndsWith ( ".framework" , StringComparison . Ordinal ) ) {
2016-04-21 15:57:02 +03:00
var add_to = WeakFrameworks ;
2016-12-23 20:50:35 +03:00
var framework = Driver . GetFrameworks ( Application ) . Find ( fwk ) ;
2016-04-21 15:57:02 +03:00
if ( framework ! = null ) {
2016-12-23 20:50:35 +03:00
if ( framework . Version > Application . SdkVersion )
2016-04-21 15:57:02 +03:00
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 )
2017-01-18 12:22:53 +03:00
args . Append ( " -u " ) . Append ( Driver . Quote ( "_" + symbol ) ) ;
2016-04-21 15:57:02 +03:00
}
2017-01-25 17:06:56 +03:00
if ( SourceFiles ! = null ) {
foreach ( var src in SourceFiles ) {
args . Append ( ' ' ) . Append ( Driver . Quote ( src ) ) ;
AddInput ( src ) ;
}
}
2016-04-21 15:57:02 +03:00
}
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" ) ;
}
2017-01-24 22:24:32 +03:00
2017-02-03 09:40:44 +03:00
if ( Application . HasAnyDynamicLibraries )
2017-01-24 22:24:32 +03:00
args . Append ( " -Xlinker -rpath -Xlinker @executable_path" ) ;
2016-04-21 15:57:02 +03:00
}
void ProcessFrameworkForArguments ( StringBuilder args , string fw , bool is_weak , ref bool any_user_framework )
{
var name = Path . GetFileNameWithoutExtension ( fw ) ;
2016-10-14 13:34:35 +03:00
if ( fw . EndsWith ( ".framework" , StringComparison . Ordinal ) ) {
2016-04-21 15:57:02 +03:00
// 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 ( ) ;
}
2017-01-25 18:38:24 +03:00
public void PopulateInputs ( )
{
var args = new StringBuilder ( ) ;
Inputs = new List < string > ( ) ;
WriteArguments ( args ) ;
}
2016-04-21 15:57:02 +03:00
}
}