2017-04-17 13:48:35 +03:00
/ /
2016-04-21 15:58:45 +03:00
// mtouch.cs: A tool to generate the necessary code to boot a Mono
// application on the iPhone
/ /
// It has a couple of modes of operation:
/ /
// COMPILATION:
/ /
// Default
/ /
// Compiles the assemblies specified and produces Assembly
// language code and C files that can be incorporated into an
// existing project.
/ /
// -sim Compile to Simulator image
/ /
// This compiles the given assemblies into the specified
// directory. The target directory can then be used as
// the contents of the .app
/ /
// -dev Compile the Device image
/ /
// This compiles the given assemblies into the specified
// directory. The target directory can then be used as
// the contents of the .app
/ /
// LAUNCHING:
// -launchsim=APP
/ /
// This launches the specified File.App in the simulator
/ /
// -debugsim=AP
/ /
// Launches the application in debug mode on the simulator
/ /
// -launchdev=ID
/ /
// Launches the specified app bundle ID on the connected device
/ /
// Copyright 2009, Novell, Inc.
// Copyright 2011-2013 Xamarin Inc. All rights reserved.
/ /
// Authors:
// Miguel de Icaza
// Geoff Norton
// Jb Evain
// Sebastien Pouliot
/ /
using System ;
using System.IO ;
using System.Diagnostics ;
using System.Linq ;
using System.Collections.Generic ;
2017-08-22 10:40:26 +03:00
using System.Globalization ;
2016-04-21 15:58:45 +03:00
using System.Text ;
using System.Threading ;
using Mono.Options ;
using Mono.Cecil ;
2017-04-17 13:48:35 +03:00
using Mono.Cecil.Cil ;
2016-04-21 15:58:45 +03:00
using Mono.Tuner ;
using MonoTouch.Tuner ;
2018-02-05 18:26:29 +03:00
using Registrar ;
using ObjCRuntime ;
2016-04-21 15:58:45 +03:00
using Xamarin.Linker ;
using Xamarin.Utils ;
2016-04-25 01:49:28 +03:00
using Xamarin.MacDev ;
2016-04-21 15:58:45 +03:00
public enum OutputFormat {
Default ,
Xml ,
}
namespace Xamarin.Bundler
{
2018-05-29 20:08:34 +03:00
public partial class Driver {
[mtouch/mmp] Fix tracking of whether the static registrar should run again or not. Fixes #641. (#3534)
* [tests] Improve debug spew for the RebuildTest_WithExtensions test.
* [mtouch/mmp] Store/load if the dynamic registrar is removed or not into the cached link results.
Store/load if the dynamic registrar is removed or not into the cached link
results, so that we generate the correct main.m even if cached linker results
are used.
* [mtouch/mmp] The static registrar must not execute if we're loading cached results from the linker.
The static registrar must not execute if we're loading cached results from the
linker, because the static registrar needs information from the linker that's
not restored from the cache.
* [mtouch/mmp] Share Touch code.
* [mtouch/mmp] Make it possible to touch inexistent files (to create them).
* [mtouch/mmp] Fix tracking of whether the static registrar should run again or not.
The recent changes to support optimizing away the dynamic registrar caused the
Xamarin.MTouch.RebuildTest_WithExtensions test to regress.
The problem
-----------
* The linker now collects and stores information the static registrar needs.
* This information is not restored from disk when the linker realizes that it
can reload previously linked assemblies instead of executing again.
* The static registrar runs again (for another reason).
* The information the static registrar needs isn't available, and incorrect
output follows.
So fix 1: show an error if the static registrar runs when the linker loaded
cached results.
The exact scenario the test ran into is this:
* 1st build: everything is new and everything is built.
* 2nd build: contents of .exe changes, the linker runs again, the static
registrar runs again, but sees that the generated output didn't change, so
it doesn't write the new content to disk (this is an optimization to avoid
compiling the registrar.m file again unless needed).
* 3rd build: only the .exe timestamp changes, the linker sees nothing changes
in the contents of the .exe and loads the previously linked assemblies from
disk, the static registrar sees that the .exe's timestamp is newer than
registrar.m's timestamp and run again, but doesn't produce the right result
because it doesn't have the information it needs.
Considered solutions
--------------------
1. Only track timestamps, not file contents. This is not ideal, since it will
result in more work done: in particular for the case above, it would add a
registrar.m compilation in build #2, and linker rerun + static registrar
rerun + registrar.m compilation + final native link in build #3.
2. Always write the output of the static registrar, even if it hasn't changed.
This is not ideal either, since it will also result in more work done: for
the case above, it would add a registrar.m compilation + final native link
in build #3.
3. Always write the output of the static registrar, but track if it changed or
not, and if it didn't, just touch registrar.o instead of recompiling it.
This only means the final native link in build #3 is added (see #5 for why
this is worse than it sounds).
4. Always write the output of the static registrar, but track it it changed or
not, and if it didn't, just touch registrar.o instead of recompiling it,
and track that too, so that the final native link in build #3 isn't needed
anymore. Unfortunately this may result in incorrect behavior, because now
the msbuild tasks will detect that the executable has changed, and may run
dsymutil + strip again. The executable didn't actually change, which means
it would be the previously stripped executable, and thus we'd end up with
an empty .dSYM because we ran dsymtil on an already stripped executable.
5. Idea #4, but write the output of the final link into a temporary directory
instead of the .app, so that we could track whether we should update the
executable in the .app or not. This is not optimal either, because
executables can be *big* (I've seen multi-GB tvOS bitcode executables), and
extra copies of such files should not be taken lightly.
6. Idea #4, but tell the MSBuild tasks that dsymutil/strip doesn't need to be
rerun even if the timestamp of the executable changed. This might actually
work, but now the solution's become quite complex.
Implemented solution
--------------------
Use stamp files to detect whether a file is up-to-date or not.
In particular:
* When we don't write to a file because the new contents are identical to the
old contents, we now touch a .stamp file. This stamp file means "the
accompanying file was determined to be up-to-date when the stamp was
touched."
* When checking whether a file is up-to-date, also check for the presence of a
.stamp file, and if it exists, use the highest timestamp between the stamp
file and the actual file.
Now the test scenario becomes:
* 1st build: everything is new and everything is built.
* 2nd build: contents of .exe changes, the linker runs again, the static
registrar runs again, but sees that the generated output didn't change, so
it doesn't write the new content to disk, but it creates a registrar.m.stamp
file to indicate the point in time when registrar.m was considered up-to-
date.
* 3rd build: only the .exe timestamp changes, the linker sees nothing changes
in the contents of the .exe and loads the previously linked assemblies from
disk, the static registrar sees that the .exe's timestamp is *older* than
registrar.m.stamp's timestamp and doesn't run again.
We only use the stamp file for source code (registrar.[m|h], main.[m|h],
pinvokes.[m|h]), since using it every time has too much potential for running
into other problems (for instance we should never create .stamp files inside
the .app).
Fixes these test failures:
1) Failed : Xamarin.MTouch.RebuildTest_WithExtensions("single","",False,System.String[])
single
Expected: <empty>
But was: < "/Users/builder/data/lanes/5746/4123bf7e/source/xamarin-macios/tests/mtouch/bin/Debug/tmp-test-dir/Xamarin.Tests.BundlerTool.CreateTemporaryDirectory371/testApp.app/testApp is modified, timestamp: 2/15/2018 3:04:11 PM > 2/15/2018 3:04:09 PM" >
2) Failed : Xamarin.MTouch.RebuildTest_WithExtensions("dual","armv7,arm64",False,System.String[])
dual
Expected: <empty>
But was: < "/Users/builder/data/lanes/5746/4123bf7e/source/xamarin-macios/tests/mtouch/bin/Debug/tmp-test-dir/Xamarin.Tests.BundlerTool.CreateTemporaryDirectory375/testApp.app/testApp is modified, timestamp: 2/15/2018 3:06:03 PM > 2/15/2018 3:06:00 PM" >
3) Failed : Xamarin.MTouch.RebuildTest_WithExtensions("llvm","armv7+llvm",False,System.String[])
llvm
Expected: <empty>
But was: < "/Users/builder/data/lanes/5746/4123bf7e/source/xamarin-macios/tests/mtouch/bin/Debug/tmp-test-dir/Xamarin.Tests.BundlerTool.CreateTemporaryDirectory379/testApp.app/testApp is modified, timestamp: 2/15/2018 3:07:14 PM > 2/15/2018 3:07:12 PM" >
4) Failed : Xamarin.MTouch.RebuildTest_WithExtensions("debug","",True,System.String[])
debug
Expected: <empty>
But was: < "/Users/builder/data/lanes/5746/4123bf7e/source/xamarin-macios/tests/mtouch/bin/Debug/tmp-test-dir/Xamarin.Tests.BundlerTool.CreateTemporaryDirectory383/testApp.app/testApp is modified, timestamp: 2/15/2018 3:08:16 PM > 2/15/2018 3:08:13 PM" >
5) Failed : Xamarin.MTouch.RebuildTest_WithExtensions("single-framework","",False,System.String[])
single-framework
Expected: <empty>
But was: < "/Users/builder/data/lanes/5746/4123bf7e/source/xamarin-macios/tests/mtouch/bin/Debug/tmp-test-dir/Xamarin.Tests.BundlerTool.CreateTemporaryDirectory387/testApp.app/testApp is modified, timestamp: 2/15/2018 3:09:18 PM > 2/15/2018 3:09:16 PM" >
Fixes https://github.com/xamarin/maccore/issues/641
2018-02-19 22:28:04 +03:00
internal const string NAME = "mtouch" ;
2016-04-21 15:58:45 +03:00
public static void ShowHelp ( OptionSet os )
{
Console . WriteLine ( "mtouch - Mono Compiler for iOS" ) ;
Console . WriteLine ( "Copyright 2009-2011, Novell, Inc." ) ;
Console . WriteLine ( "Copyright 2011-2016, Xamarin Inc." ) ;
Console . WriteLine ( "Usage is: mtouch [options]" ) ;
os . WriteOptionDescriptions ( Console . Out ) ;
}
enum Action {
None ,
Help ,
Version ,
Build ,
InstallSim ,
LaunchSim ,
DebugSim ,
InstallDevice ,
LaunchDevice ,
DebugDevice ,
KillApp ,
KillAndLaunch ,
ListDevices ,
ListSimulators ,
IsAppInstalled ,
ListApps ,
LogDev ,
RunRegistrar ,
ListCrashReports ,
DownloadCrashReport ,
KillWatchApp ,
LaunchWatchApp ,
2017-04-12 12:00:43 +03:00
Embeddinator ,
2016-04-21 15:58:45 +03:00
}
/ /
// Output generation
2019-02-20 08:30:08 +03:00
static string dotfile ;
2016-04-21 15:58:45 +03:00
static string cross_prefix = Environment . GetEnvironmentVariable ( "MONO_CROSS_PREFIX" ) ;
static string extra_args = Environment . GetEnvironmentVariable ( "MTOUCH_ENV_OPTIONS" ) ;
2019-02-20 08:30:08 +03:00
public static string DotFile {
2017-01-24 17:04:59 +03:00
get {
2019-02-20 08:30:08 +03:00
return dotfile ;
2017-01-24 17:04:59 +03:00
}
}
2016-04-21 15:58:45 +03:00
public static bool IsUnified {
2016-09-30 22:02:17 +03:00
get { return true ; }
2016-04-21 15:58:45 +03:00
}
2020-05-08 10:17:55 +03:00
public static string GetArchDirectory ( Application app , bool is64bit )
{
if ( is64bit )
return GetArch64Directory ( app ) ;
return GetArch32Directory ( app ) ;
}
2016-12-23 20:50:35 +03:00
public static string GetArch32Directory ( Application app )
{
switch ( app . Platform ) {
case ApplePlatform . iOS :
2020-05-08 10:17:55 +03:00
case ApplePlatform . WatchOS :
2016-12-23 20:50:35 +03:00
return Path . Combine ( GetPlatformFrameworkDirectory ( app ) , ".." , ".." , "32bits" ) ;
default :
2020-01-31 23:02:52 +03:00
throw ErrorHelper . CreateError ( 71 , Errors . MX0071 , app . Platform , "Xamarin.iOS" ) ;
2016-11-01 18:41:41 +03:00
}
}
2016-12-23 20:50:35 +03:00
public static string GetArch64Directory ( Application app )
{
switch ( app . Platform ) {
case ApplePlatform . iOS :
2020-05-08 10:17:55 +03:00
case ApplePlatform . TVOS :
2016-12-23 20:50:35 +03:00
return Path . Combine ( GetPlatformFrameworkDirectory ( app ) , ".." , ".." , "64bits" ) ;
default :
2020-01-31 23:02:52 +03:00
throw ErrorHelper . CreateError ( 71 , Errors . MX0071 , app . Platform , "Xamarin.iOS" ) ;
2016-04-21 15:58:45 +03:00
}
}
// This is for the -mX-version-min=A.B compiler flag
2016-12-23 20:50:35 +03:00
public static string GetTargetMinSdkName ( Application app )
{
switch ( app . Platform ) {
case ApplePlatform . iOS :
return app . IsDeviceBuild ? "iphoneos" : "ios-simulator" ;
case ApplePlatform . WatchOS :
return app . IsDeviceBuild ? "watchos" : "watchos-simulator" ;
case ApplePlatform . TVOS :
return app . IsDeviceBuild ? "tvos" : "tvos-simulator" ;
default :
2020-01-31 23:02:52 +03:00
throw ErrorHelper . CreateError ( 71 , Errors . MX0071 , app . Platform , "Xamarin.iOS" ) ;
2016-04-21 15:58:45 +03:00
}
}
2016-12-23 20:50:35 +03:00
public static bool GetLLVMAsmWriter ( Application app )
{
if ( app . LLVMAsmWriter . HasValue )
return app . LLVMAsmWriter . Value ;
switch ( app . Platform ) {
case ApplePlatform . iOS :
return false ;
case ApplePlatform . TVOS :
case ApplePlatform . WatchOS :
return true ;
default :
2020-01-31 23:02:52 +03:00
throw ErrorHelper . CreateError ( 71 , Errors . MX0071 , app . Platform , "Xamarin.iOS" ) ;
2016-04-21 15:58:45 +03:00
}
}
2016-12-23 20:50:35 +03:00
public static bool IsUsingClang ( Application app )
{
return app . CompilerPath . EndsWith ( "clang" , StringComparison . Ordinal ) | | app . CompilerPath . EndsWith ( "clang++" , StringComparison . Ordinal ) ;
2016-04-21 15:58:45 +03:00
}
2019-09-24 18:24:49 +03:00
public static string GetAotCompiler ( Application app , Abi abi , bool is64bits )
2016-04-21 15:58:45 +03:00
{
switch ( app . Platform ) {
case ApplePlatform . iOS :
if ( is64bits ) {
return Path . Combine ( cross_prefix , "bin" , "arm64-darwin-mono-sgen" ) ;
} else {
return Path . Combine ( cross_prefix , "bin" , "arm-darwin-mono-sgen" ) ;
}
case ApplePlatform . WatchOS :
2019-09-24 18:24:49 +03:00
/* Use arm64_32 cross only for Debug mode */
if ( abi = = Abi . ARM64_32 & & ! app . EnableLLVMOnlyBitCode ) {
return Path . Combine ( cross_prefix , "bin" , "arm64_32-darwin-mono-sgen" ) ;
} else {
return Path . Combine ( cross_prefix , "bin" , "armv7k-unknown-darwin-mono-sgen" ) ;
}
2016-04-21 15:58:45 +03:00
case ApplePlatform . TVOS :
2018-02-16 12:45:22 +03:00
return Path . Combine ( cross_prefix , "bin" , "arm64-darwin-mono-sgen" ) ;
2016-04-21 15:58:45 +03:00
default :
2020-01-31 23:02:52 +03:00
throw ErrorHelper . CreateError ( 71 , Errors . MX0071 , app . Platform , "Xamarin.iOS" ) ;
2016-04-21 15:58:45 +03:00
}
}
2019-10-14 17:18:46 +03:00
public static IList < string > GetAotArguments ( Application app , string filename , Abi abi , string outputDir , string outputFile , string llvmOutputFile , string dataFile )
2016-04-21 15:58:45 +03:00
{
string fname = Path . GetFileName ( filename ) ;
2019-10-14 17:18:46 +03:00
var args = new List < string > ( ) ;
2016-04-21 15:58:45 +03:00
bool enable_llvm = ( abi & Abi . LLVM ) ! = 0 ;
bool enable_thumb = ( abi & Abi . Thumb ) ! = 0 ;
bool enable_debug = app . EnableDebug ;
2017-04-17 13:48:35 +03:00
bool enable_debug_symbols = app . PackageManagedDebugSymbols ;
2016-04-21 15:58:45 +03:00
bool llvm_only = app . EnableLLVMOnlyBitCode ;
2018-10-10 18:02:28 +03:00
bool interp = app . IsInterpreted ( Assembly . GetIdentity ( filename ) ) ;
2019-02-25 17:24:34 +03:00
bool interp_full = ! interp & & app . UseInterpreter ;
2018-10-10 18:02:28 +03:00
bool is32bit = ( abi & Abi . Arch32Mask ) > 0 ;
2016-04-21 15:58:45 +03:00
string arch = abi . AsArchString ( ) ;
2019-10-14 17:18:46 +03:00
args . Add ( "--debug" ) ;
2016-04-21 15:58:45 +03:00
if ( enable_llvm )
2019-10-14 17:18:46 +03:00
args . Add ( "--llvm" ) ;
2016-04-21 15:58:45 +03:00
2018-05-05 00:59:31 +03:00
if ( ! llvm_only & & ! interp )
2019-10-14 17:18:46 +03:00
args . Add ( "-O=gsharedvt" ) ;
if ( app . AotOtherArguments ! = null )
args . AddRange ( app . AotOtherArguments ) ;
var aot = new StringBuilder ( ) ;
aot . Append ( "--aot=mtriple=" ) ;
aot . Append ( enable_thumb ? arch . Replace ( "arm" , "thumb" ) : arch ) ;
aot . Append ( "-ios," ) ;
aot . Append ( "data-outfile=" ) . Append ( dataFile ) . Append ( "," ) ;
aot . Append ( app . AotArguments ) ;
2016-04-21 15:58:45 +03:00
if ( llvm_only )
2019-10-14 17:18:46 +03:00
aot . Append ( "llvmonly," ) ;
2018-10-10 18:02:28 +03:00
else if ( interp ) {
if ( fname ! = "mscorlib.dll" )
2020-01-31 23:02:52 +03:00
throw ErrorHelper . CreateError ( 99 , Errors . MX0099 , fname ) ;
2019-10-14 17:18:46 +03:00
aot . Append ( "interp," ) ;
2018-10-10 18:02:28 +03:00
} else if ( interp_full ) {
2019-10-14 17:18:46 +03:00
aot . Append ( "interp,full," ) ;
2018-10-10 18:02:28 +03:00
} else
2019-10-14 17:18:46 +03:00
aot . Append ( "full," ) ;
2016-04-21 15:58:45 +03:00
var aname = Path . GetFileNameWithoutExtension ( fname ) ;
var sdk_or_product = Profile . IsSdkAssembly ( aname ) | | Profile . IsProductAssembly ( aname ) ;
if ( enable_llvm )
2019-10-14 17:18:46 +03:00
aot . Append ( "nodebug," ) ;
2017-04-17 13:48:35 +03:00
else if ( ! ( enable_debug | | enable_debug_symbols ) )
2019-10-14 17:18:46 +03:00
aot . Append ( "nodebug," ) ;
2016-12-23 20:50:35 +03:00
else if ( app . DebugAll | | app . DebugAssemblies . Contains ( fname ) | | ! sdk_or_product )
2019-10-14 17:18:46 +03:00
aot . Append ( "soft-debug," ) ;
2016-04-21 15:58:45 +03:00
2019-10-14 17:18:46 +03:00
aot . Append ( "dwarfdebug," ) ;
2016-04-21 15:58:45 +03:00
/* Needed for #4587 */
if ( enable_debug & & ! enable_llvm )
2019-10-14 17:18:46 +03:00
aot . Append ( "no-direct-calls," ) ;
2016-04-21 15:58:45 +03:00
if ( ! app . UseDlsym ( filename ) )
2019-10-14 17:18:46 +03:00
aot . Append ( "direct-pinvoke," ) ;
2016-04-21 15:58:45 +03:00
2016-07-18 02:58:40 +03:00
if ( app . EnableMSym ) {
2019-10-14 17:18:46 +03:00
var msymdir = Path . Combine ( outputDir , "Msym" ) ;
aot . Append ( $"msym-dir={msymdir}," ) ;
2016-07-18 02:58:40 +03:00
}
2016-04-21 15:58:45 +03:00
if ( enable_llvm )
2020-08-06 17:10:06 +03:00
aot . Append ( "llvm-path=" ) . Append ( GetFrameworkCurrentDirectory ( app ) ) . Append ( "/LLVM/bin/," ) ;
2016-04-21 15:58:45 +03:00
2019-10-14 17:18:46 +03:00
aot . Append ( "outfile=" ) . Append ( outputFile ) ;
2016-04-21 15:58:45 +03:00
if ( enable_llvm )
2019-10-14 17:18:46 +03:00
aot . Append ( ",llvm-outfile=" ) . Append ( llvmOutputFile ) ;
args . Add ( aot . ToString ( ) ) ;
args . Add ( filename ) ;
return args ;
2016-04-21 15:58:45 +03:00
}
public static void CopyAssembly ( string source , string target , string target_dir = null )
{
if ( File . Exists ( target ) )
File . Delete ( target ) ;
if ( target_dir = = null )
target_dir = Path . GetDirectoryName ( target ) ;
if ( ! Directory . Exists ( target_dir ) )
Directory . CreateDirectory ( target_dir ) ;
File . Copy ( source , target ) ;
string sconfig = source + ".config" ;
string tconfig = target + ".config" ;
if ( File . Exists ( tconfig ) )
File . Delete ( tconfig ) ;
if ( File . Exists ( sconfig ) )
File . Copy ( sconfig , tconfig ) ;
string tdebug = target + ".mdb" ;
if ( File . Exists ( tdebug ) )
File . Delete ( tdebug ) ;
string sdebug = source + ".mdb" ;
if ( File . Exists ( sdebug ) )
File . Copy ( sdebug , tdebug ) ;
2017-01-09 18:19:04 +03:00
string tpdb = Path . ChangeExtension ( target , "pdb" ) ;
if ( File . Exists ( tpdb ) )
File . Delete ( tpdb ) ;
string spdb = Path . ChangeExtension ( source , "pdb" ) ;
if ( File . Exists ( spdb ) )
File . Copy ( spdb , tpdb ) ;
2016-04-21 15:58:45 +03:00
}
2016-12-23 20:50:35 +03:00
public static bool SymlinkAssembly ( Application app , string source , string target , string target_dir )
2016-04-21 15:58:45 +03:00
{
if ( app . IsSimulatorBuild & & Driver . XcodeVersion > = new Version ( 6 , 0 ) ) {
// Don't symlink with Xcode 6, it has a broken simulator (at least in
// that doesn't work properly when installing/upgrading apps with symlinks.
return false ;
}
if ( File . Exists ( target ) )
File . Delete ( target ) ;
if ( ! Directory . Exists ( target_dir ) )
Directory . CreateDirectory ( target_dir ) ;
if ( ! Symlink ( source , target ) )
return false ;
string sconfig = source + ".config" ;
string tconfig = target + ".config" ;
if ( File . Exists ( tconfig ) )
File . Delete ( tconfig ) ;
if ( File . Exists ( sconfig ) )
Symlink ( sconfig , tconfig ) ;
string tdebug = target + ".mdb" ;
if ( File . Exists ( tdebug ) )
File . Delete ( tdebug ) ;
string sdebug = source + ".mdb" ;
if ( File . Exists ( sdebug ) )
Symlink ( sdebug , tdebug ) ;
2017-02-07 16:39:17 +03:00
string tpdb = Path . ChangeExtension ( target , "pdb" ) ;
if ( File . Exists ( tpdb ) )
File . Delete ( tpdb ) ;
string spdb = Path . ChangeExtension ( source , "pdb" ) ;
if ( File . Exists ( spdb ) )
Symlink ( spdb , tpdb ) ;
2016-04-21 15:58:45 +03:00
return true ;
}
2016-12-23 20:50:35 +03:00
public static bool CanWeSymlinkTheApplication ( Application app )
2016-04-21 15:58:45 +03:00
{
if ( app . Platform ! = ApplePlatform . iOS )
return false ;
// We can not symlink when building an extension.
if ( app . IsExtension )
return false ;
// We can not symlink if we have to link with user frameworks.
if ( app . Frameworks . Count > 0 | | app . UseMonoFramework . Value )
return false ;
foreach ( var target in app . Targets ) {
foreach ( var assembly in target . Assemblies )
if ( assembly . Frameworks ! = null & & assembly . Frameworks . Count > 0 )
return false ;
}
//We can only symlink when running in the simulation
if ( ! app . IsSimulatorBuild )
return false ;
//mtouch was invoked with --nofastsim, eg symlinking was explicit disabled
2016-12-23 20:50:35 +03:00
if ( app . NoFastSim )
2016-04-21 15:58:45 +03:00
return false ;
//Can't symlink if we are running the linker since the assemblies content will change
if ( app . LinkMode ! = LinkMode . None )
return false ;
//Custom gcc flags requires us to build template.m
2019-10-14 17:18:46 +03:00
if ( app . UserGccFlags ? . Count > 0 )
2016-04-21 15:58:45 +03:00
return false ;
// Setting environment variables is done in the generated main.m, so we can't symlink in this case.
2016-12-23 20:50:35 +03:00
if ( app . EnvironmentVariables . Count > 0 )
2016-04-21 15:58:45 +03:00
return false ;
2017-03-15 23:59:08 +03:00
// If we are asked to run with concurrent sgen we also need to pass environment variables
if ( app . EnableSGenConc )
return false ;
2018-05-05 00:59:31 +03:00
if ( app . UseInterpreter )
return false ;
2016-09-30 22:02:17 +03:00
if ( app . Registrar = = RegistrarMode . Static )
2016-04-21 15:58:45 +03:00
return false ;
2016-05-13 14:16:37 +03:00
// The default exception marshalling differs between release and debug mode, but we
// only have one simlauncher, so to use the simlauncher we'd have to chose either
// debug or release mode. Debug is more frequent, so make that the fast path.
if ( ! app . EnableDebug )
2016-05-11 13:47:26 +03:00
return false ;
2016-05-13 14:16:37 +03:00
if ( app . MarshalObjectiveCExceptions ! = MarshalObjectiveCExceptionMode . UnwindManagedCode )
return false ; // UnwindManagedCode is the default for debug builds.
2017-04-12 14:23:33 +03:00
if ( app . Embeddinator )
return false ;
2016-04-21 15:58:45 +03:00
return true ;
}
2017-01-03 21:45:16 +03:00
static Application ParseArguments ( string [ ] args , out Action a )
2016-04-21 15:58:45 +03:00
{
2017-01-02 16:41:14 +03:00
var action = Action . None ;
2017-01-31 12:18:45 +03:00
var app = new Application ( args ) ;
2016-04-21 15:58:45 +03:00
[mtouch/mmp] Improve target framework code. (#8137)
* Unify target framework code between mtouch and mmp.
* Simplify the code in mmp: have three possible valid target frameworks for
most of code, and add special code to handle setting any other valid target
frameworks to redirect to one of those three valid target frameworks (and
warn if given any of those valid, but not "main", target frameworks). Any
other code can then depend on the target framework having exactly one of
those specific values, which means we can make IsUnified* variables
convenience properties instead.
* Unify a bit more of the argument parsing code between mtouch and mmp, since
that made a few other things easier.
* Add TargetFramework.IsValidFramework to have one validation implementation.
* Move the implementation of TargetFramework.MonoFrameworkDirectory to mmp
itself, it's not really related to the target framework.
* Remove Driver.IsUnified and IsClassic from mmp, they're not used anymore.
* Formally deprecate --xamarin-[full|system]-framework in mmp, they've really been deprecated for many years.
* Remove LinkerOptions.TargetFramework, it's not used anymore.
* Get rid of mmp's userTargetFramework fried, it's duplicated with the
targetFramework field.
* Add a few tests, and tweak others a bit.
Breaking changes:
* Both mtouch and mmp require --target-framework now. The only direct
consumers should be the MSBuild tasks, which already pass --target-framework
all the time. This simplifies code, and removes assumptions.
2020-03-19 11:28:09 +03:00
a = Action . None ;
2016-04-21 15:58:45 +03:00
if ( extra_args ! = null ) {
var l = new List < string > ( args ) ;
foreach ( var s in extra_args . Split ( new char [ ] { ' ' } , StringSplitOptions . RemoveEmptyEntries ) )
l . Add ( s ) ;
args = l . ToArray ( ) ;
}
2017-01-02 16:41:14 +03:00
Action < Action > SetAction = ( Action value ) = >
{
switch ( action ) {
case Action . None :
action = value ;
break ;
case Action . KillApp :
if ( value = = Action . LaunchDevice ) {
action = Action . KillAndLaunch ;
break ;
}
goto default ;
case Action . LaunchDevice :
if ( value = = Action . KillApp ) {
action = Action . KillAndLaunch ;
break ;
}
goto default ;
default :
2020-05-14 17:45:05 +03:00
throw new ProductException ( 19 , true , Errors . MT0019 ) ;
2017-01-02 16:41:14 +03:00
}
} ;
2016-04-21 15:58:45 +03:00
var os = new OptionSet ( ) {
2019-02-20 08:30:08 +03:00
{ "dot:" , "Generate a dot file to visualize the build tree." , v = > dotfile = v ? ? string . Empty } ,
2016-04-21 15:58:45 +03:00
{ "aot=" , "Arguments to the static compiler" ,
2016-12-23 20:50:35 +03:00
v = > app . AotArguments = v + ( v . EndsWith ( "," , StringComparison . Ordinal ) ? String . Empty : "," ) + app . AotArguments
2016-04-21 15:58:45 +03:00
} ,
{ "aot-options=" , "Non AOT arguments to the static compiler" ,
v = > {
if ( v . Contains ( "--profile" ) | | v . Contains ( "--attach" ) )
throw new Exception ( "Unsupported flag to -aot-options" ) ;
2019-10-14 17:18:46 +03:00
if ( ! StringUtils . TryParseArguments ( v , out var aot_options , out var ex ) )
2020-01-31 23:02:52 +03:00
throw ErrorHelper . CreateError ( 26 , ex , Errors . MX0026 , "-aot-options=" + v , ex . Message ) ;
2019-10-14 17:18:46 +03:00
if ( app . AotOtherArguments = = null )
app . AotOtherArguments = new List < string > ( ) ;
app . AotOtherArguments . AddRange ( aot_options ) ;
2016-04-21 15:58:45 +03:00
}
} ,
{ "gsharedvt:" , "Generic sharing for value-types - always enabled [Deprecated]" , v = > { } } ,
2018-05-29 20:08:34 +03:00
{ "time" , v = > WatchLevel + + } ,
2016-04-21 15:58:45 +03:00
{ "executable=" , "Specifies the native executable name to output" , v = > app . ExecutableName = v } ,
2016-12-23 20:50:35 +03:00
{ "nofastsim" , "Do not run the simulator fast-path build" , v = > app . NoFastSim = true } ,
2017-01-24 13:10:20 +03:00
{ "nodevcodeshare" , "Do not share native code between extensions and main app." , v = > app . NoDevCodeShare = true } ,
2018-09-10 23:42:42 +03:00
{ "nodebugtrack" , "Disable debug tracking of object resurrection bugs [DEPRECATED - already disabled by default]" , v = > app . DebugTrack = false , true } ,
2016-04-21 15:58:45 +03:00
{ "linkerdumpdependencies" , "Dump linker dependencies for linker-analyzer tool" , v = > app . LinkerDumpDependencies = true } ,
{ "nolinkaway" , "Disable the linker step which replace code with a 'Linked Away' exception." , v = > app . LinkAway = false } ,
{ "symbollist=" , "Asks mtouch to create a file with all the symbols that should not be stripped instead of stripping." , v = > app . SymbolList = v , true /* hidden, this is only used between mtouch and the MSBuild tasks, not for public consumption */ } ,
{ "nostrip" , "Do not strip the AOT compiled assemblies" , v = > app . ManagedStrip = false } ,
{ "nosymbolstrip:" , "A comma-separated list of symbols to not strip from the app (if the list is empty, stripping is disabled completely)" , v = >
{
if ( string . IsNullOrEmpty ( v ) ) {
app . NativeStrip = false ;
} else {
app . NoSymbolStrip . AddRange ( v . Split ( ',' ) ) ;
}
} } ,
{ "dsym:" , "Turn on (default for device) or off (default for simulator) .dSYM symbols." , v = > app . BuildDSym = ParseBool ( v , "dsym" ) } ,
{ "dlsym:" , "Use dlsym to resolve pinvokes in AOT compiled assemblies" , v = > app . ParseDlsymOptions ( v ) } ,
2020-05-19 16:30:38 +03:00
{ "r|ref=" , "Add an assembly to the resolver [DEPRECATED, use --reference instead]" , v = > app . References . Add ( v ) , true /* Hide: this option is deprecated in favor of the shared --reference option instead */ } ,
2019-10-14 17:18:46 +03:00
{ "gcc_flags=" , "Set flags to be passed along to gcc at link time" , v = >
{
if ( ! StringUtils . TryParseArguments ( v , out var gcc_flags , out var ex ) )
2020-01-31 23:02:52 +03:00
throw ErrorHelper . CreateError ( 26 , ex , Errors . MX0026 , "-gcc-flags=" + v , ex . Message ) ;
2019-10-14 17:18:46 +03:00
if ( app . UserGccFlags = = null )
app . UserGccFlags = new List < string > ( ) ;
app . UserGccFlags . AddRange ( gcc_flags ) ;
}
} ,
2016-04-21 15:58:45 +03:00
{ "framework=" , "Link with the specified framework. This can either be a system framework (like 'UIKit'), or it can be a path to a custom framework ('/path/to/My.framework'). In the latter case the entire 'My.framework' directory is copied into the app as well." , ( v ) = > app . Frameworks . Add ( v ) } ,
{ "weak-framework=" , "Weak link with the specified framework. This can either be a system framework (like 'UIKit'), or it can be a path to a custom framework ('/path/to/My.framework'). In the latter case the entire 'My.framework' directory is copied into the app as well." , ( v ) = > app . Frameworks . Add ( v ) } ,
// What we do
{ "sim=" , "Compile for the Simulator, specify the output directory for code" , v = >
{
SetAction ( Action . Build ) ;
2016-12-23 20:50:35 +03:00
app . AppDirectory = v ;
2016-04-21 15:58:45 +03:00
app . BuildTarget = BuildTarget . Simulator ;
}
} ,
{ "dev=" , "Compile for the Device, specify the output directory for the code" , v = > {
SetAction ( Action . Build ) ;
2016-12-23 20:50:35 +03:00
app . AppDirectory = v ;
2016-04-21 15:58:45 +03:00
app . BuildTarget = BuildTarget . Device ;
}
} ,
// Configures the tooling used to build code.
{ "device=" , "Specifies the device type to launch the simulator as [DEPRECATED]" , v = > { } , true } ,
// Launch/debug options
{ "timeout:" , "Specify a timeout (in seconds) for the commands that doesn't have fixed/known duration. Default: 0.5 [DEPRECATED]" , v = > { } , true } ,
{ "listapps" , "List the apps installed on the device to stderr. [DEPRECATED]" , v = > { SetAction ( Action . ListApps ) ; } , true } ,
{ "listsim=" , "List the available simulators. The output is xml, and written to the specified file. [DEPRECATED]" , v = > { SetAction ( Action . ListSimulators ) ; } , true } ,
{ "listdev:" , "List the currently connected devices and their UDIDs [DEPRECATED]" , v = > { SetAction ( Action . ListDevices ) ; } , true } ,
{ "installdev=" , "Install the specified MonoTouch.app in the device [DEPRECATED]" , v = > { SetAction ( Action . InstallDevice ) ; } , true } ,
{ "launchdev=" , "Launch an app that is installed on device, specified by bundle identifier [DEPRECATED]" , v = > { SetAction ( Action . LaunchDevice ) ; } , true } ,
{ "debugdev=" , "Launch app app that is installed on device, specified by bundle identifer, and wait for a native debugger to attach. [DEPRECATED]" , v = > { SetAction ( Action . DebugDevice ) ; } , true } ,
{ "provide-assets=" , "If the app contains on-demand resources, then mtouch should provide those from this .app directory. [DEPRECATED]" , v = > { } , true } ,
2018-04-05 11:53:20 +03:00
{ "wait-for-exit:" , "If mtouch should wait until the launched app exits. [DEPRECATED]" , v = > { } , true } ,
2016-04-21 15:58:45 +03:00
{ "wait-for-unlock:" , "If mtouch should wait until the device is unlocked when launching an app (or exit with an error code). [DEPRECATED]" , v = > { } , true } ,
{ "killdev=" , "Kill an app that is running on device, specified by bundle identifier [DEPRECATED]" , v = > { SetAction ( Action . KillApp ) ; } , true } ,
{ "logdev" , "Write the syslog from the device to the console [DEPRECATED]" , v = > { SetAction ( Action . LogDev ) ; } , true } ,
{ "list-crash-reports:" , "Lists crash reports on the specified device [DEPRECATED]" , v = > { SetAction ( Action . ListCrashReports ) ; } , true } ,
{ "download-crash-report=" , "Download a crash report from the specified device [DEPRECATED]" , v = > { SetAction ( Action . DownloadCrashReport ) ; } , true } ,
{ "download-crash-report-to=" , "Specifies the file to save the downloaded crash report. [DEPRECATED]" , v = > { } , true } ,
{ "devname=" , "Specify which device (when many are present) the [install|lauch|kill|log]dev command applies [DEPRECATED]" , v = > { } , true } ,
{ "isappinstalled=" , "Check if the specified app id is installed. Returns 0 when installed, 1 if not. [DEPRECATED]" , v = > { SetAction ( Action . IsAppInstalled ) ; } , true } ,
{ "launchsim=" , "Launch the specified MonoTouch.app in the simulator [DEPRECATED]" , v = > { SetAction ( Action . LaunchSim ) ; } , true } ,
{ "enable-native-debugging:" , "Don't do anything that may interfere with native debugging (such as avoiding the iOS simulator launch timeout). [DEPRECATED]" , v = > { } , true } ,
{ "installsim=" , "Install the specified MonoTouch.app in the simulator [DEPRECATED]" , v = > { SetAction ( Action . InstallSim ) ; } , true } ,
{ "launchsimwatch=" , "Specify the watch app to launch [DEPRECATED]" , v = > { SetAction ( Action . LaunchWatchApp ) ; } , true } ,
{ "killsimwatch=" , "Specify the watch app to kill [DEPRECATED]" , v = > { SetAction ( Action . KillWatchApp ) ; } , true } ,
{ "watchnotificationpayload=" , "Specify the jSON notification payload file [DEPRECATED]" , v = > { } , true } ,
{ "watchlaunchmode=" , "Specify the watch launch mode (Default|Glance|Notification) [DEPRECATED]" , v = > { } , true } ,
{ "enable-background-fetch" , "Enable mode to send background fetch requests [Deprecated]" , v = > { } , true } ,
{ "launch-for-background-fetch" , "Launch due to a background fetch [DEPRECATED]" , v = > { } , true } ,
{ "debugsim=" , "Debug the specified MonoTouch.app in the simulator [DEPRECATED]" , v = > { SetAction ( Action . DebugSim ) ; } , true } ,
{ "argument=" , "Launch the app with this command line argument. This must be specified multiple times for multiple arguments [DEPRECATED]" , v = > { } , true } ,
{ "sgen:" , "Enable the SGen garbage collector" ,
v = > {
if ( ! ParseBool ( v , "sgen" ) )
2020-01-31 23:02:52 +03:00
ErrorHelper . Warning ( 43 , Errors . MX0043 ) ;
2016-04-21 15:58:45 +03:00
} ,
true // do not show the option anymore
} ,
{ "boehm:" , "Enable the Boehm garbage collector" ,
v = > {
if ( ParseBool ( v , "boehm" ) )
2020-01-31 23:02:52 +03:00
ErrorHelper . Warning ( 43 , Errors . MX0043 ) ; } ,
2016-04-21 15:58:45 +03:00
true // do not show the option anymore
} ,
{ "new-refcount:" , "Enable new refcounting logic" ,
v = > {
if ( ! ParseBool ( v , "new-refcount" ) )
2020-01-31 23:02:52 +03:00
ErrorHelper . Warning ( 80 , Errors . MX0080 ) ;
2016-04-21 15:58:45 +03:00
} ,
true // do not show the option anymore
} ,
{ "override-abi=" , "Override any previous abi. Only used for testing." , v = > { app . ClearAbi ( ) ; app . ParseAbi ( v ) ; } , true } , // Temporary command line arg until XS has better support for 64bit architectures.
{ "cxx" , "Enable C++ support" , v = > { app . EnableCxx = true ; } } ,
2019-03-19 16:22:39 +03:00
{ "enable-repl:" , "Enable REPL support. For simulator only and disabling linking is recommended." , v = > { app . EnableRepl = ParseBool ( v , "enable-repl" ) ; } } ,
2016-04-21 15:58:45 +03:00
{ "pie:" , "Enable (default) or disable PIE (Position Independent Executable)." , v = > { app . EnablePie = ParseBool ( v , "pie" ) ; } } ,
2016-12-23 20:50:35 +03:00
{ "compiler=" , "Specify the Objective-C compiler to use (valid values are gcc, g++, clang, clang++ or the full path to a GCC-compatible compiler)." , v = > { app . Compiler = v ; } } ,
2017-01-25 22:03:30 +03:00
{ "fastdev" , "Build an app that supports fastdev (this app will only work when launched using Xamarin Studio)" , v = > { app . AddAssemblyBuildTarget ( "@all=dynamiclibrary" ) ; } } ,
2018-04-30 16:33:48 +03:00
{ "force-thread-check" , "Keep UI thread checks inside (even release) builds [DEPRECATED, use --optimize=-remove-uithread-checks instead]" , v = > { app . Optimizations . RemoveUIThreadChecks = false ; } , true } ,
{ "disable-thread-check" , "Remove UI thread checks inside (even debug) builds [DEPRECATED, use --optimize=remove-uithread-checks instead]" , v = > { app . Optimizations . RemoveUIThreadChecks = true ; } , true } ,
2020-05-13 09:40:07 +03:00
{ "package-mdb:" , "Specify whether debug info files (*.mdb) should be packaged in the app. Default is 'true' for debug builds and 'false' for release builds. [DEPRECATED]" , v = > app . PackageManagedDebugSymbols = ParseBool ( v , "package-mdb" ) , true } ,
2016-04-21 15:58:45 +03:00
{ "msym:" , "Specify whether managed symbolication files (*.msym) should be created. Default is 'false' for debug builds and 'true' for release builds." , v = > app . EnableMSym = ParseBool ( v , "msym" ) } ,
{ "extension" , v = > app . IsExtension = true } ,
{ "app-extension=" , "The path of app extensions that are included in the app. This must be specified once for each app extension." , v = > app . Extensions . Add ( v ) , true /* MSBuild-internal for now */ } ,
{ "stderr=" , "Redirect the standard error for the simulated application to the specified file [DEPRECATED]" , v = > { } , true } ,
{ "stdout=" , "Redirect the standard output for the simulated application to the specified file [DEPRECATED]" , v = > { } , true } ,
2016-12-08 20:59:04 +03:00
2016-04-21 15:58:45 +03:00
{ "mono:" , "Comma-separated list of options for how the Mono runtime should be included. Possible values: 'static' (link statically), 'framework' (linked as a user framework), '[no-]package-framework' (if the Mono.framework should be copied to the app bundle or not. The default value is 'framework' for extensions, and main apps if the app targets iOS 8.0 or later and contains extensions, otherwise 'static'. The Mono.framework will be copied to the app bundle if mtouch detects it's needed, but this may be overridden if the default values for 'framework' vs 'static' is overwridden." , v = >
{
foreach ( var opt in v . Split ( new char [ ] { ',' } ) ) {
2016-11-09 18:16:00 +03:00
switch ( opt ) {
2016-04-21 15:58:45 +03:00
case "static" :
app . UseMonoFramework = false ;
break ;
case "framework" :
app . UseMonoFramework = true ;
break ;
case "package-framework" :
app . PackageMonoFramework = true ;
break ;
case "no-package-framework" :
app . PackageMonoFramework = false ;
break ;
default :
2020-05-14 17:45:05 +03:00
throw new ProductException ( 20 , true , Errors . MX0020 , "--mono" , "static, framework or [no-]package-framework" ) ;
2016-04-21 15:58:45 +03:00
}
}
}
} ,
{ "bitcode:" , "Enable generation of bitcode (asmonly, full, marker)" , v = >
{
switch ( v ) {
case "asmonly" :
app . BitCodeMode = BitCodeMode . ASMOnly ;
break ;
case "full" :
app . BitCodeMode = BitCodeMode . LLVMOnly ;
break ;
case "marker" :
app . BitCodeMode = BitCodeMode . MarkerOnly ;
break ;
default :
2020-05-14 17:45:05 +03:00
throw new ProductException ( 20 , true , Errors . MX0020 , "--bitcode" , "asmonly, full or marker" ) ;
2016-04-21 15:58:45 +03:00
}
}
} ,
2016-12-23 20:50:35 +03:00
{ "llvm-asm" , "Make the LLVM compiler emit assembly files instead of object files. [Deprecated]" , v = > { app . LLVMAsmWriter = true ; } , true } ,
2017-01-20 18:11:48 +03:00
{ "llvm-opt=" , "Specify how to optimize the LLVM output (only applicable when using LLVM to compile to bitcode), per assembly: 'assembly'='optimizations', where 'assembly is the filename (including extension) of the assembly (the special value 'all' can be passed to set the same optimization for all assemblies), and 'optimizations' are optimization arguments. Valid optimization flags are Clang optimization flags." , v = >
{
var equals = v . IndexOf ( '=' ) ;
if ( equals = = - 1 )
2020-01-31 23:02:52 +03:00
throw ErrorHelper . CreateError ( 26 , Errors . MX0026 , "-llvm-opt=" + v , "Both assembly and optimization must be specified (assembly=optimization)" ) ;
2017-01-20 18:11:48 +03:00
var asm = v . Substring ( 0 , equals ) ;
var opt = v . Substring ( equals + 1 ) ; // An empty string is valid here, meaning 'no optimizations'
app . LLVMOptimizations [ asm ] = opt ;
}
} ,
2018-10-10 18:02:28 +03:00
{ "interpreter:" , "Enable the *experimental* interpreter. Optionally takes a comma-separated list of assemblies to interpret (if prefixed with a minus sign, the assembly will be AOT-compiled instead). 'all' can be used to specify all assemblies. This argument can be specified multiple times." , v = >
{
app . UseInterpreter = true ;
if ( ! string . IsNullOrEmpty ( v ) ) {
app . InterpretedAssemblies . AddRange ( v . Split ( new char [ ] { ',' } , StringSplitOptions . RemoveEmptyEntries ) ) ;
}
}
} ,
2016-04-21 15:58:45 +03:00
{ "output-format=" , "Specify the output format for some commands. Possible values: Default, XML" , v = >
{
}
} ,
2020-02-19 17:23:52 +03:00
{ "xamarin-framework-directory=" , "The framework directory" , v = > { framework_dir = v ; } , true } ,
2017-01-24 19:55:28 +03:00
{ "assembly-build-target=" , "Specifies how to compile assemblies to native code. Possible values: 'staticobject' (default), 'dynamiclibrary' and 'framework'. " +
"Each option also takes an assembly and a potential name (defaults to the name of the assembly). Example: --assembly-build-target=mscorlib.dll=framework[=name]." +
"There also two special names: '@all' and '@sdk': --assembly-build-target=@all|@sdk=framework[=name]." , v = >
{
app . AddAssemblyBuildTarget ( v ) ;
}
} ,
2020-05-19 16:30:38 +03:00
/* Any new options that are identical between mtouch and mmp should be added to common/Driver.cs */
2016-04-21 15:58:45 +03:00
} ;
[mtouch/mmp] Improve target framework code. (#8137)
* Unify target framework code between mtouch and mmp.
* Simplify the code in mmp: have three possible valid target frameworks for
most of code, and add special code to handle setting any other valid target
frameworks to redirect to one of those three valid target frameworks (and
warn if given any of those valid, but not "main", target frameworks). Any
other code can then depend on the target framework having exactly one of
those specific values, which means we can make IsUnified* variables
convenience properties instead.
* Unify a bit more of the argument parsing code between mtouch and mmp, since
that made a few other things easier.
* Add TargetFramework.IsValidFramework to have one validation implementation.
* Move the implementation of TargetFramework.MonoFrameworkDirectory to mmp
itself, it's not really related to the target framework.
* Remove Driver.IsUnified and IsClassic from mmp, they're not used anymore.
* Formally deprecate --xamarin-[full|system]-framework in mmp, they've really been deprecated for many years.
* Remove LinkerOptions.TargetFramework, it's not used anymore.
* Get rid of mmp's userTargetFramework fried, it's duplicated with the
targetFramework field.
* Add a few tests, and tweak others a bit.
Breaking changes:
* Both mtouch and mmp require --target-framework now. The only direct
consumers should be the MSBuild tasks, which already pass --target-framework
all the time. This simplifies code, and removes assumptions.
2020-03-19 11:28:09 +03:00
if ( ParseOptions ( app , os , args , ref action ) )
return null ;
2016-04-21 15:58:45 +03:00
2017-01-03 21:45:16 +03:00
a = action ;
2016-04-21 15:58:45 +03:00
app . SetDefaultAbi ( ) ;
2017-01-03 21:45:16 +03:00
if ( action = = Action . Build | | action = = Action . RunRegistrar ) {
2017-11-16 18:02:34 +03:00
if ( app . RootAssemblies . Count = = 0 )
2020-05-14 17:45:05 +03:00
throw new ProductException ( 17 , true , Errors . MX0017 ) ;
2017-01-03 21:45:16 +03:00
}
return app ;
}
static int Main2 ( string [ ] args )
{
Action action ;
var app = ParseArguments ( args , out action ) ;
if ( app = = null )
return 0 ;
2018-11-05 10:16:29 +03:00
// Allow a few actions, since these seem to always work no matter the Xcode version.
var accept_any_xcode_version = action = = Action . ListDevices | | action = = Action . ListCrashReports | | action = = Action . ListApps | | action = = Action . LogDev ;
2020-08-06 17:10:06 +03:00
ValidateXcode ( app , accept_any_xcode_version , false ) ;
2016-04-21 15:58:45 +03:00
2020-08-17 09:37:29 +03:00
if ( IsMlaunchAction ( action ) )
2020-08-06 17:10:06 +03:00
return CallMlaunch ( app ) ;
2016-04-21 15:58:45 +03:00
2016-12-23 20:50:35 +03:00
if ( app . SdkVersion = = null )
2020-05-14 17:45:05 +03:00
throw new ProductException ( 25 , true , Errors . MT0025 , app . PlatformName ) ;
2016-12-23 19:09:39 +03:00
2016-12-23 20:50:35 +03:00
var framework_dir = GetFrameworkDirectory ( app ) ;
2018-09-14 20:12:59 +03:00
Driver . Log ( "Xamarin.iOS {0}.{1} using framework: {2}" , Constants . Version , Constants . Revision , framework_dir ) ;
2016-04-21 15:58:45 +03:00
if ( action = = Action . None )
2020-05-14 17:45:05 +03:00
throw new ProductException ( 52 , true , Errors . MT0052 ) ;
2016-04-21 15:58:45 +03:00
2016-12-23 20:50:35 +03:00
if ( app . SdkVersion < new Version ( 6 , 0 ) & & app . IsArchEnabled ( Abi . ARMv7s ) )
2020-05-14 17:45:05 +03:00
throw new ProductException ( 14 , true , Errors . MT0014 , app . SdkVersion , "ARMv7s" ) ;
2016-04-21 15:58:45 +03:00
2016-12-23 20:50:35 +03:00
if ( app . SdkVersion < new Version ( 7 , 0 ) & & app . IsArchEnabled ( Abi . ARM64 ) )
2020-05-14 17:45:05 +03:00
throw new ProductException ( 14 , true , Errors . MT0014 , app . SdkVersion , "ARM64" ) ;
2016-04-21 15:58:45 +03:00
2016-12-23 20:50:35 +03:00
if ( app . SdkVersion < new Version ( 7 , 0 ) & & app . IsArchEnabled ( Abi . x86_64 ) )
2020-05-14 17:45:05 +03:00
throw new ProductException ( 14 , true , Errors . MT0014 , app . SdkVersion , "x86_64" ) ;
2016-04-21 15:58:45 +03:00
if ( ! Directory . Exists ( framework_dir ) ) {
Console . WriteLine ( "Framework does not exist {0}" , framework_dir ) ;
2016-12-23 20:50:35 +03:00
Console . WriteLine ( " Platform = {0}" , GetPlatform ( app ) ) ;
Console . WriteLine ( " SDK = {0}" , app . SdkVersion ) ;
2016-04-21 15:58:45 +03:00
Console . WriteLine ( " Deployment Version: {0}" , app . DeploymentTarget ) ;
}
2016-12-23 20:50:35 +03:00
if ( ! Directory . Exists ( GetPlatformDirectory ( app ) ) )
2020-05-14 17:45:05 +03:00
throw new ProductException ( 6 , true , Errors . MT0006 , GetPlatformDirectory ( app ) ) ;
2016-04-21 15:58:45 +03:00
2016-12-23 20:50:35 +03:00
if ( ! Directory . Exists ( app . AppDirectory ) )
2016-12-24 18:57:08 +03:00
Directory . CreateDirectory ( app . AppDirectory ) ;
2016-04-21 15:58:45 +03:00
if ( app . EnableRepl & & app . BuildTarget ! = BuildTarget . Simulator )
2020-05-14 17:45:05 +03:00
throw new ProductException ( 29 , true , Errors . MT0029 ) ;
2016-04-21 15:58:45 +03:00
if ( app . EnableRepl & & app . LinkMode ! = LinkMode . None )
2020-05-14 17:45:05 +03:00
throw new ProductException ( 82 , true , Errors . MT0082 ) ;
2016-04-21 15:58:45 +03:00
if ( cross_prefix = = null )
2020-08-06 17:10:06 +03:00
cross_prefix = GetFrameworkCurrentDirectory ( app ) ;
2016-04-21 15:58:45 +03:00
Watch ( "Setup" , 1 ) ;
if ( action = = Action . RunRegistrar ) {
app . RunRegistrar ( ) ;
2017-01-03 21:45:16 +03:00
} else if ( action = = Action . Build ) {
2017-01-03 21:45:42 +03:00
if ( app . IsExtension & & ! app . IsWatchExtension ) {
var sb = new StringBuilder ( ) ;
foreach ( var arg in args )
sb . AppendLine ( arg ) ;
File . WriteAllText ( Path . Combine ( Path . GetDirectoryName ( app . AppDirectory ) , "build-arguments.txt" ) , sb . ToString ( ) ) ;
} else {
foreach ( var appex in app . Extensions ) {
var f_path = Path . Combine ( appex , ".." , "build-arguments.txt" ) ;
if ( ! File . Exists ( f_path ) )
continue ;
Action app_action ;
2018-02-21 11:27:26 +03:00
var ext = ParseArguments ( File . ReadAllLines ( f_path ) , out app_action ) ;
ext . ContainerApp = app ;
app . AppExtensions . Add ( ext ) ;
2017-01-03 21:45:42 +03:00
if ( app_action ! = Action . Build )
2020-01-31 23:02:52 +03:00
throw ErrorHelper . CreateError ( 99 , Errors . MX0099 , $"Extension build action is '{app_action}' when it should be 'Build'" ) ;
2017-01-03 21:45:42 +03:00
}
2017-01-24 13:10:20 +03:00
app . BuildAll ( ) ;
2017-01-03 21:45:42 +03:00
}
2017-01-03 21:45:16 +03:00
} else {
2020-01-31 23:02:52 +03:00
throw ErrorHelper . CreateError ( 99 , Errors . MX0099 , action ) ;
2016-04-21 15:58:45 +03:00
}
return 0 ;
}
2020-08-17 09:37:29 +03:00
static bool IsMlaunchAction ( Action action )
{
switch ( action ) {
/* Device actions */
case Action . LogDev :
case Action . InstallDevice :
case Action . ListDevices :
case Action . IsAppInstalled :
case Action . ListCrashReports :
case Action . DownloadCrashReport :
case Action . KillApp :
case Action . KillAndLaunch :
case Action . LaunchDevice :
case Action . DebugDevice :
case Action . ListApps :
/* Simulator actions */
case Action . DebugSim :
case Action . LaunchSim :
case Action . InstallSim :
case Action . LaunchWatchApp :
case Action . KillWatchApp :
case Action . ListSimulators :
return true ;
}
return false ;
}
2016-04-21 15:58:45 +03:00
static void RedirectStream ( StreamReader @in , StreamWriter @out )
{
new Thread ( ( ) = >
{
string line ;
while ( ( line = @in . ReadLine ( ) ) ! = null ) {
@out . WriteLine ( line ) ;
@out . Flush ( ) ;
}
} )
{ IsBackground = true } . Start ( ) ;
}
2020-08-06 17:10:06 +03:00
static string GetMlaunchPath ( Application app )
{
// check next to mtouch first
var path = Path . Combine ( GetFrameworkBinDirectory ( app ) , "mlaunch" ) ;
if ( File . Exists ( path ) )
return path ;
2016-04-21 15:58:45 +03:00
2020-08-06 17:10:06 +03:00
// check an environment variable
path = Environment . GetEnvironmentVariable ( "MLAUNCH_PATH" ) ;
if ( File . Exists ( path ) )
return path ;
2016-04-21 15:58:45 +03:00
2020-08-06 17:10:06 +03:00
throw ErrorHelper . CreateError ( 93 , Errors . MT0093 ) ;
2016-04-21 15:58:45 +03:00
}
2020-08-06 17:10:06 +03:00
static int CallMlaunch ( Application app )
2016-04-21 15:58:45 +03:00
{
Log ( 1 , "Forwarding to mlaunch" ) ;
using ( var p = new Process ( ) ) {
p . StartInfo . UseShellExecute = false ;
p . StartInfo . RedirectStandardError = true ;
p . StartInfo . RedirectStandardInput = true ;
p . StartInfo . RedirectStandardOutput = true ;
2020-08-06 17:10:06 +03:00
p . StartInfo . FileName = GetMlaunchPath ( app ) ;
2016-04-21 15:58:45 +03:00
2019-10-14 17:18:46 +03:00
var sb = Environment . GetCommandLineArgs ( ) . Skip ( 1 ) . ToList ( ) ;
p . StartInfo . Arguments = StringUtils . FormatArguments ( sb ) ;
2016-04-21 15:58:45 +03:00
p . Start ( ) ;
RedirectStream ( new StreamReader ( Console . OpenStandardInput ( ) ) , p . StandardInput ) ;
RedirectStream ( p . StandardOutput , new StreamWriter ( Console . OpenStandardOutput ( ) ) ) ;
RedirectStream ( p . StandardError , new StreamWriter ( Console . OpenStandardError ( ) ) ) ;
p . WaitForExit ( ) ;
2016-09-29 18:45:23 +03:00
2016-04-21 15:58:45 +03:00
return p . ExitCode ;
}
}
2016-12-23 20:50:35 +03:00
static string FindGcc ( Application app , bool gpp )
2016-04-21 15:58:45 +03:00
{
2016-12-23 20:50:35 +03:00
var usr_bin = Path . Combine ( GetPlatformDirectory ( app ) , GetPlatform ( app ) + ".platform" , "Developer" , "usr" , "bin" ) ;
2016-04-21 15:58:45 +03:00
var gcc = ( gpp ? "g++" : "gcc" ) ;
var compiler_path = Path . Combine ( usr_bin , gcc + "-4.2" ) ;
if ( ! File . Exists ( compiler_path ) ) {
// latest iOS5 beta (6+) do not ship with a gcc-4.2 symlink - see bug #346
compiler_path = Path . Combine ( usr_bin , gcc ) ;
}
return compiler_path ;
}
2016-12-23 20:50:35 +03:00
public static void CalculateCompilerPath ( Application app )
2016-04-21 15:58:45 +03:00
{
var fallback_to_clang = false ;
var original_compiler = string . Empty ;
/ /
// The default is gcc, but if we can't find gcc,
// we try again with clang.
/ /
2016-12-23 20:50:35 +03:00
if ( string . IsNullOrEmpty ( app . Compiler ) ) {
2016-04-21 15:58:45 +03:00
// by default we use `gcc` before iOS7 SDK, falling back to `clang`. Otherwise we go directly to `clang`
// so we don't get bite by the fact that Xcode5 has a gcc compiler (which calls `clang`, even if not 100%
// compitable wrt options) for the simulator but not for devices!
// ref: https://bugzilla.xamarin.com/show_bug.cgi?id=13838
if ( app . Platform = = ApplePlatform . iOS ) {
2016-12-23 20:50:35 +03:00
fallback_to_clang = app . SdkVersion < new Version ( 7 , 0 ) ;
2016-04-21 15:58:45 +03:00
} else {
fallback_to_clang = false ;
}
if ( fallback_to_clang )
2016-12-23 20:50:35 +03:00
app . Compiler = app . EnableCxx ? "g++" : "gcc" ;
2016-04-21 15:58:45 +03:00
else
2016-12-23 20:50:35 +03:00
app . Compiler = app . EnableCxx ? "clang++" : "clang" ;
2016-04-21 15:58:45 +03:00
}
tryagain :
2016-12-23 20:50:35 +03:00
switch ( app . Compiler ) {
2016-04-21 15:58:45 +03:00
case "clang++" :
2016-12-23 20:50:35 +03:00
app . CompilerPath = Path . Combine ( DeveloperDirectory , "Toolchains" , "XcodeDefault.xctoolchain" , "usr" , "bin" , "clang++" ) ;
2016-04-21 15:58:45 +03:00
break ;
case "clang" :
2016-12-23 20:50:35 +03:00
app . CompilerPath = Path . Combine ( DeveloperDirectory , "Toolchains" , "XcodeDefault.xctoolchain" , "usr" , "bin" , "clang" ) ;
2016-04-21 15:58:45 +03:00
break ;
case "gcc" :
2016-12-23 20:50:35 +03:00
app . CompilerPath = FindGcc ( app , false ) ;
2016-04-21 15:58:45 +03:00
break ;
case "g++" :
2016-12-23 20:50:35 +03:00
app . CompilerPath = FindGcc ( app , true ) ;
2016-04-21 15:58:45 +03:00
break ;
default : // This is the full path to a compiler.
2016-12-23 20:50:35 +03:00
app . CompilerPath = app . Compiler ;
2016-04-21 15:58:45 +03:00
break ;
}
2016-12-23 20:50:35 +03:00
if ( ! File . Exists ( app . CompilerPath ) ) {
2016-04-21 15:58:45 +03:00
if ( fallback_to_clang ) {
// Couldn't find gcc, try to find clang.
2016-12-23 20:50:35 +03:00
original_compiler = app . Compiler ;
app . Compiler = app . EnableCxx ? "clang++" : "clang" ;
2016-04-21 15:58:45 +03:00
fallback_to_clang = false ;
goto tryagain ;
}
if ( string . IsNullOrEmpty ( original_compiler ) ) {
2020-05-14 17:45:05 +03:00
throw new ProductException ( 5101 , true , Errors . MT5101 , app . Compiler ) ;
2016-04-21 15:58:45 +03:00
} else {
2020-05-14 17:45:05 +03:00
throw new ProductException ( 5103 , true , Errors . MT5103 , app . Compiler , original_compiler ) ;
2016-04-21 15:58:45 +03:00
}
}
}
}
}