Question

Code reuse across .NET and Monotouch has been addressed in many ways, but most techniques involve parallel sets of projects/solutions.

I'd like this question to clarify whether it is possible to build, using a single csproj file, either a .NET or a MonoTouch version of the same assembly, based on the selected Platform and Configuration.

Assume the following:

  1. The build machine runs Windows.
  2. Visual Studio and MSBuild are available.
  3. The method does not need to work on MonoDevelop, since MonoDevelop currently does not have XBuild/MSBuild turned on for MonoTouch.
  4. The MonoTouch binaries from a valid MonoTouch installation are available.
  5. Only a library needs to be built, not a full app. This library:
    1. Does not contain any .xib's or plists.
    2. Does use platform specific code, which will be conditionally compiled.
    3. Does reference platform specific assemblies (necessarily), and these need to be handled by the .csproj file.

Question: Given these assumptions, can the library be

  1. Compiled for Windows .NET or
  2. Compiled for direct referencing by a MonoTouch app solution after the binary is copied to a Mac.

based on the current Visual Studio Platform and Configuration?

Current Research

  1. The fact that Portable libraries compiled on Windows can be consumed by MonoTouch solutions gives some hope that this is possible.
  2. The use of <NoStdLib>true</NoStdLib> in the .csproj while building for MonoTouch, together with different HintPaths for System, System.Core, System.Xml etc seem necessary.
  3. Unsure about how to reference the correct version of mscorlib while building for MonoTouch.
Was it helpful?

Solution

Turns out this is in fact quite possible with MSBuild. The key points, assuming you're starting out with a C# project created for Windows .NET that you want to also compile for iOS are as follows. Note: this does not work with MonoDevelop. It only works with Visual Studio. This is because MonoDevelop does not currently use a full-fledged MSBuild compatible build system for MonoTouch.

  1. Create a new solution and project platform for iOS, named iOS. This is used to compile the project for MonoTouch. You can name this anything you want, but use the name consistently in what follows.
  2. For the iOS platform, set NoStdLib to true to avoid automatically referencing mscorlib.dll. Also set the path to a directory that contains local copies of the MonoTouch dll's, copied from your MonoTouch installation on a Mac:

    <PropertyGroup Condition="'$(Platform'=='iOS'">
      <NoStdLib>true</NoStdLib>
      <iOSLibs>c:\MonoTouch\</iOSLibs>
    </PropertyGroup>`
    
  3. Add references to local copies of all referenced MonoTouch assemblies from the folder:

    <ItemGroup Condition="'$(Platform'=='iOS'">
      <Reference Include="mscorlib">
        <HintPath>$(iOSLibs)\mscorlib.dll</HintPath>
      </Reference>    
      <Reference Include="System">
        <HintPath>$(iOSLibs)\System.dll</HintPath>
      </Reference>    
      <Reference Include="System.Core">
        <HintPath>$(iOSLibs)\System.Core.dll</HintPath>
      </Reference>    
      <Reference Include="System.Xml"> 
        <HintPath>$(iOSLibs)\System.Xml.dll</HintPath>
      </Reference>
      <Reference Include="System.Xml.Linq"> 
        <HintPath>$(iOSLibs)\System.Xml.Linq.dll</HintPath>
      </Reference>
      <Reference Include="monotouch">
        <HintPath>$(iOSLibs)\monotouch.dll</HintPath>
      </Reference>    
    </ItemGroup>
    
  4. Visual Studio automatically adds an ItemGroup referencing all the .NET assemblies. Since we've instructed Visual Studio/MSBuild to get these from the iOSLibs folder when building for iOS, we should conditionally disable the default assemblies. This stops VS from complaining about duplicate assembly references. Change the first line of the ItemGroup and add the condition as follows. (Note: the list of assemblies will differ depending on what your project currently references.)

    <ItemGroup Condition=" '$(Platform)' != 'iOS' ">
      <Reference Include="System" />
      <Reference Include="System.Core" />
      <Reference Include="System.Drawing" />
      <Reference Include="System.Windows.Forms" />
      <Reference Include="System.Xml.Linq" />
      <Reference Include="System.Data.DataSetExtensions" />
      <Reference Include="Microsoft.CSharp" />
      <Reference Include="System.Data" />
      <Reference Include="System.Xml" />
    </ItemGroup>
    

Now you're ready to go. Once you have done this for all the projects in your solution, you can select iOS from the Solution drop down and build MonoTouch binary-compatible assemblies like magic, without the use of Portable Class Libraries! (This does not mean that PCLs are not useful, they certainly are, but the technique shown here is much more generally applicable.)

A more detailed knowledge of MSBuild and its considerable power will help you simplify even more, e.g.:

  1. Moving much of the above into a common MSBuild file that you can reference from all your project files to eliminate duplication.
  2. Defining compiler constants using <DefineConstants>...</DefineConstants> to allow your code to do things differently based on the current solution and configuration.

OTHER TIPS

Technically, it should be possible to rewrite the generated assembly to target a different platform, using tools like https://github.com/jbevain/cecil, but I don't think it's worth the (immense) effort. Dissassembling and rebuilding is another (expensive) option.

If your question is about retargeting a assembly provided by a third party, and this is your only plan, make sure you don't breach any licensing terms.

Now, if you can strip some of your assumptions out, and use a different build system (like rake or make), you can also achieve code sharing without PCL at buildfile level.

Hope it helps.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top