A Short Introductory Guide to the Microsoft code name “M” Modeling Language (Part 2 of 2) [Complete article available in Word Format (196K) for nice printing]
Compiling "M"
Of course, having a bunch of "M" code sitting around is well and good, but to make all those types, extents, and functions useful they must be turned into actual database constructs.
This basically means compiling the "M" code and generating output that can be used to populate a database. For this there are two options:
- Compile "M" from the command-line using the m.exe tool (generally found within Program Files\Microsoft Oslo\1.0\bin)
- Compile "M" from within Visual Studio using the "MCompile" element in a project file. (This option is installed automatically with the "Oslo" SDK. This option instructs MSBuild.exe to invoke m.exe automatically.
Note: it is also possible to dynamically parse and compile "M" programmatically, but that's beyond the scope of this article.
The "M" compiler, as shown in the diagram below, simply parses the "M" and generates some kind of output that can be used to create a database. Typically this is an "M" image (or MX) file that can then be fed to the image loading utility, mx.exe, for deployment into a database. (At which point it becomes accessible to applications or runtimes that make use of that database. Note that "Repository" is shown in this diagram but can be any SQL Server 2008 database.)
Alternately, you can use the "M" compiler to generate SQL statements that can then be deployed using the sqlcmd.exe command-line tool. Let’s look at these different compilation methods in turn. We’ll come back to the matter of installation/deployment later.
Compiling "M" on the Command Line with Defaults
Assuming that you have a file hello_m.m with the contents shown earlier, the command line to compile it is simply this:
m hello_m.m
By default this produces hello_m.mx (an image file). You can use flags like –out: to specify a different output filename and –p: to specify a different output types (see next section). For the full list of options, run m -?.
Of course, you may have errors in your "M" code, in which case the compiler, behaving as a compiler should, issues appropriate warnings. For example, the following code specifies an integer value (42) for a field of type Text:
//type_error.m
module ShowErrors
{
type Person
{
Name : Text;
Age : Text { 42 }
}
}
Compiling this will give the error:
type_error.m(8,22): error M0152: Literal '42' cannot be converted to the type 'Text'.
Putting the value 42 in quotes ("42") to make it a string, or changing the type of Age to Integer32 will correct the error.
Compiling "M" on the Command Line with SQL Outputs
To generate SQL statements from "M", specify the –p:script option on the command line (the default is –p:image):
m hello_m.m –p:script
This will create hello_m.sql (or another filename specified with –out:). Run this command and take a look at the SQL output, which we’ll do in a moment within the context of "Intellipad".
It’s worth noting that if an "M" file contains only types, such as this:
//noextents.m
module NoExtents
{
type OrderItem
{
Description : Text;
Quantity : Number;
}
}
the generated SQL script will be empty. As noted before, while a type might define a table, an extent is necessary to create a table (that is, declare storage).
Note also that not all "M" types map directly to SQL. Compiling this code, for instance:
//nomapping.m
module NoMapping
{
type OrderItem
{
Description : Text;
Quantity : Number;
}
OrderItems : {OrderItem*};
}
will give this error:
nomapping.m(7,9): error M2026: SQL generation does not support the type 'External: Number'.
In these cases, choose a different type for the field in question.
Generating SQL from "M" in "Intellipad"
The "Intellipad" tool included with "Oslo" offers many rich features with working with "M", one of which is the ability to view generated SQL as you directly type "M". Launch "Intellipad", open up hello_m.m as we’ve been using, then select the M Mode -> T-SQL Preview menu command and you’ll see a SQL-preview pane appear on the right:
This T-SQL Preview mode is very helpful to explore the relationships between various "M" constructs and the SQL generated for them. You can see in the above screenshot, for example, how the Height and Weight constraints in the Size type manifest in SQL as the Check_Sizes_Func function and trigger that invokes it within the Sizes table (the last line on the lower right of the image). You can also see how the identity and AutoNumber() keywords for the Id field in the Size table manifest in SQL as an identity field along with a primary key constraint.
"Intellipad" also offers syntax checking and IntelliSense™ for "M". Opening our previous type_error.m file, for example, gives the same error as the compiler:
"Intellipad" offers many more features, of course, but those are beyond the scope of this particular article.
Compiling "M" within Visual Studio
Visual Studio supports a native "M" project type:
Within any project too, you can add or import an "M" file:
The "M" file type supports a "Compile" build action (in the properties pane for that item). This build action generates both .mx and .sql files for each .m item in the project that can be found in the bin\Debug output folder.
Here’s what an "M" project file for Visual Studio looks like with the most important sections highlighted:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<MTarget>TSQL10</MTarget>
<MPackageScript>true</MPackageScript>
<MPackageImage>true</MPackageImage>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<MTargetsPath Condition="$(MTargetsPath) == ''">$(MSBuildExtensionsPath32)\Microsoft\M\v1.0</MTargetsPath>
<ProjectName>Models1</ProjectName>
<RootNamespace>Models1</RootNamespace>
<AssemblyName>Models1</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<Name>Models1</Name>
<ProjectGuid>{6eb7381e-1457-4731-94be-c9f18d0811a3}</ProjectGuid>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<OutputPath>bin\Debug\</OutputPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<OutputPath>bin\Release\</OutputPath>
</PropertyGroup>
<ItemGroup>
<Compile Include="Model.m" />
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<Import Project="$(MTargetsPath)\Microsoft.M.targets" />
</Project>
<Project ...>
Note that here you can control whether to output .mx or .sql files individually with the MPackageScript and MPackageImage elements.
Multiple M Files and Image Files
Now obviously, any project of significance will have many "M" files to define its data models, so "M" readily supports referencing types between different "M" modules. In the "M" language itself, types and extents found in other modules are referenced using an import statement. For example, the following module imports the types from MyModule in our hello_m.m and contains the SmallSize type refinement shown earlier:
module SecondModule
{
import MyModule;
type SmallSize : Size
{
Height : Integer32 where value < 6;
Weight : Integer32 where value < 120 => 100;
}
}
Of course, compiling this "M" file by itself will produce errors given that the type Size is unknown. ("Intellipad" too, at present, doesn’t automatically resolve imported types, and will give show errors.) But that type does exist within hello_m.m. First, that type has to be exported within hello_m.m:
module MyModule
{
export Size;
...
The compiled image file is then referenced on the m.exe command line (with /reference or /r) when compiling the .m file that needs it:
m.exe secondmodule.m /r:hello_m.mx
Within am "M" project file for visual studio, the necessary image file is specified with an MReference tag:
<ItemGroup>
<Compile Include="hello_m.m" />
<MReference Include="size.mx" />
</ItemGroup>
Deploying/Installing/Loading "M" Into a Database
Once "M" code has been compiled into an image file or SQL script, deployment to a database is a simple step.
With an "M" image file, use the mx.exe image loader utility:
mx.exe install hello_m.mx size.mx -f -d:Repository
where install is a necessary command option after which you can specify any number of image filenames. Additional flags control various aspects of the installation, such as –f (which forces existing tables and other database elements to be dropped) and –d (which specifies the name of the target database). For complete details, use mx -?.
SQL scripts, of course, can be executed using sqlcmd.exe which will also install everything into a database.
Conclusion
And once everything is in the database, you can use any data access technology you like to work with that data. That is, use of the "Oslo" repository does not impose any restrictions or requirements upon your data access method. The "Oslo" repository is a SQL Server database, so anything that gets you to SQL Server is sufficient. Applications and runtimes as shown in the earlier diagram can likewise be written using any suitable platform technology, whether .NET, WPF, raw Windows API, and so forth. The "M" language is simply a convenient and powerful way to define the structure of database in a purposefully-designed "modeling" language.
M in a Nutshell.docx (196.45 kb)