One of the most common problems that people face when
porting applications from Windows to Linux using Mono are
paths.
The Problem
Windows developers are used to a case-insensitive file
system, which means that they might create a file called
"mydata" in one place, and try to access it somewhere else as
"MyData" or as "MYDATA". This of course breaks on most Unix
setups because Windows is case insensitive[1].
Another problem is that developers on Windows are known to
hardcode the directory separator character in their source
code ("\") instead of using
Path.DirectorySeparator and using
Path.Combine for combining this paths. This is a
problem because "\" is a valid file name components on Unix.
This means that if an application hardcodes for example
"Logs\access_log", in Unix this will not store the contents in
the "Logs" directory as the file "access_log". Instead, it
will store the results in a file called "Logs\access_log".
Only a few applications cope with drive letters, but they
might still pose a problem as the colon is a valid filename in
Unix, which means that "A:\file" is a valid filename in the
current directory.
Although .NET provides the tools to write code that is
portable, in practice, they do not use these features (the
exception is Path.Combine, which some people use, as it is
genuinely useful on its own).
The Usual Solution
When moving applications from Windows to Linux, it is
always necessary to run the application, run its test suite,
and validate that the application works as intended. With the
path problems described above, the process above included a
number of iterations to fix the assumptions made by
programmers about the file system.
This process could be time consuming, because identifying
where the mistakes were made could take some time, the program
might fail with FileNotFound exceptions (when referencing
files that were not there), data would show up empty (listing
contents of a directory that had nothing, as all the data went
elsewhere) but it was doable.
This process works as long as you have the source code to
all the components that you are porting, but if you were using
a third-party library that you had no source code for, you
would not be able to fix the problems.
The New Solution
This week, Dick Porter introduced a portability layer into
Mono that will address those problems without requiring
changes to your code. This will remove a large component of
the porting cycle as a whole class of obnoxious problems are
gone.
The new portability framework is enabled by setting the
environment variable MONO_IOMAP (which
we will likely rename to something shorter) to one of the
following values:
- case: makes all file system access case
insensitive.
- drive: strips drive name from pathnames.
- all: enables both case and
drive.
In addition, if any of those options are enabled, the
directory separator mapping is also turned on. So this
basically means that you have to type this, or include this in
your script that launches your application:
$ export MONO_IOMAP=all
$ mono myapp.exe
For ASP.NET applications hosted with mod_mono, you can add
the following directive to your Apache configuration file:
MonoSetEnv MONO_IOMAP=all
This new feature will appear in Mono 1.1.18.
The downside is that Mono will have to do some extra work
when coping with your file system, to search for case
insensitive file names. So if your application is still a
portable application, you will be much better off without this
switch.
[1] Some Linux file systems are case insensitive, and some
folks have used a combination of hacks, including doing
loopback CIFS mounts to get case sensitivity issues out of the
way; OS X does not have this particular problem, but it
still has the others.