Skip to main content

mathlink or wstp - Portable makefile for wscc builds


What's the canonical way to write a portable makefile for wscc programs? The makefile typically requires the location of the wscc script and wstp.h header file.


I see in this example they are hardcoded:



WSTPLINKDIR = /usr/local/Wolfram/Mathematica/10.0/SystemFiles/Links/WSTP/DeveloperKit
SYS = Linux # Set this value with the result of evaluating $SystemID
CADDSDIR = ${WSTPLINKDIR}/${SYS}/CompilerAdditions

Can we do something better? For ordinary linux packages, I'd use e.g.,pkg-config. Do I just have to tell anyone who wants to build my code to search for the correct directories (possibly with locate) and hack the makefile themselves?


Is there a way within a Mathematica shell to get the location of them reliably? Even GetEnvironment[{"MATHEMATICA_HOME"}] isn't enough.



Answer



There is no platform-independent way to detect the location of Mathematica from a terminal shell or Makefile. You also have to decide which version to use when multiple versions are installed (a fairly common situation). For a few platform-dependent attempts to detect locations, take a look at the makefiles we used for MATLink. None of these ways are robust though.


However, if you are creating a Mathematica package with some binary components, and all the C source code is contained within the package (without hard-to-control external dependencies, such as MATLAB was for MATLink), then you can compile from within Mathematica. The installation directory is pointed to by $InstallationDirectory. All the relevant paths (such as the MathLink libraries) are at a fixed location relative to that.


The CCompilerDriver package seems to have much of this automated. It takes care not only of the location of MathLink libraries, but also most platform-specific or compiler-specific details (such as different syntax for gcc and MSVC). See the "WSTP Executables" section in its documentation. Note that you must use MathLink instead of WSTP if you are working with CCompilerDriver.



A nice solution is to set up your package to auto-compile its binary components on first load. If you do this, keep in mind that you can only count on your users having a C compiler installed on Linux. On OS X and Windows it is better to ship pre-compiled executables.




Skeleton project that auto-compiles its MathLink component


The following is a simple proof-of-concept of a package that auto-compiles its MathLink dependency using CreateExecutable. For simple projects, it is sufficient to use CreateExecutable, and you do not need a Makefile. The advantage is that it will take care of most OS-specific or compiler-specific details, so you will not need to have separate makefiles for different operating systems / compilers.


To set up the project, create the following directory structure


AddTwo
|
|- Kernel
| |
| \- init.m

|
|- AddTwo.m
|
|- Sources
| |
| |- addtwo.tm
| |
| \- addtwo.c
|
\- addtwoprog

|
\

Here addtwoprog must be an empty directory. This is where the MathLink executables will be saved.


The parent of the top-level AddTwo directory (not AddTwo itself!) must be in the $Path.


addtwo.tm and addtwo.c must be copied from the location opened by


SystemOpen@
FileNameJoin[{$InstallationDirectory, "SystemFiles", "Links",
"MathLink", "DeveloperKit", $SystemID, "MathLinkExamples"}]


If this location contains addtwo.cxx instead of addtwo.c, simply rename it to addtwo.c after copying.


This is the contents of the other files:


(* init.m *)
Get["AddTwo`AddTwo`"]

(* AddTwo.m *)
BeginPackage["AddTwo`"]

(* CCompilerDriver` is not included in BeginPackage to avoid keeping it in the $ContextPath *)
Needs["CCompilerDriver`"]


(* Must mention AddTwo symbol from addtwo.tm here
to ensure it is created in the correct context. *)
AddTwo::usage = "AddTwo[m, n] returns the sum of integers m and n.";

Begin["`Private`"]

$packageDir = DirectoryName[$InputFileName];
$executable = FileNameJoin[{$packageDir, "addtwoprog"}]; (* See Install[] documentation, Details section, for how it resolves directories into executables *)


(* This is the list of source files to be compiled. There may be multiple .c and .tm files,
all of which will be linked together. See the CreateExecutable[] documentation,
Details section, on how to include additional object files. See CreateObjectFile[]
on how to create an object file without also linking it into an executable. *)
$sourceFiles = FileNameJoin[{$packageDir, "Sources", #}]& /@ {"addtwo.tm", "addtwo.c"};

(* Just a colourful Print alternative *)
print[args___] := Print @@ (Style[#, Red]&) /@ {args}

If[

Install[$executable] === $Failed
,
print["Compiling AddTwo executable..."];
CreateExecutable[$sourceFiles, "addtwoprog",
"TargetDirectory" -> FileNameJoin[{$executable, $SystemID}],

(* the following two options are here to ease debugging *)
"ShellCommandFunction" -> Print, (* see compilation command *)
"ShellOutputFunction" -> Print (* see compiler messages *)
];

If[
Install[$executable] === $Failed
,
print["Failed to compile or run AddTwo executable. \[SadSmiley]"],
print["Compiling successful! \[HappySmiley]"]
]
]

End[] (* `Private` *)


EndPackage[]

This package will auto-compile its C component when it is loaded for the first time.


Try it with


<
?AddTwo

AddTwo[3,4]


The compiled binary will be stored in the package directory. On a second load of the package, it won't be re-compiled again. The best approach is probably to distribute these binaries to your users. This is especially important on OS X and Windows where you can't count on a compiler being already set up. Luckily, on these platforms it is easier to ensure that your binaries will work for everyone (e.g. see here for OS X).


This skeleton package was tested on OS X / M11.1 and Linux / Raspberry Pi / M11.0.


Comments

Popular posts from this blog

plotting - Filling between two spheres in SphericalPlot3D

Manipulate[ SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, Mesh -> None, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], {n, 0, 1}] I cant' seem to be able to make a filling between two spheres. I've already tried the obvious Filling -> {1 -> {2}} but Mathematica doesn't seem to like that option. Is there any easy way around this or ... Answer There is no built-in filling in SphericalPlot3D . One option is to use ParametricPlot3D to draw the surfaces between the two shells: Manipulate[ Show[SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], ParametricPlot3D[{ r {Sin[t] Cos[1.5 Pi], Sin[t] Sin[1.5 Pi], Cos[t]}, r {Sin[t] Cos[0 Pi], Sin[t] Sin[0 Pi], Cos[t]}}, {r, 1, 2 - n}, {t, 0, Pi}, PlotStyle -> Yellow, Mesh -> {2, 15}]], {n, 0, 1}]

plotting - Plot 4D data with color as 4th dimension

I have a list of 4D data (x position, y position, amplitude, wavelength). I want to plot x, y, and amplitude on a 3D plot and have the color of the points correspond to the wavelength. I have seen many examples using functions to define color but my wavelength cannot be expressed by an analytic function. Is there a simple way to do this? Answer Here a another possible way to visualize 4D data: data = Flatten[Table[{x, y, x^2 + y^2, Sin[x - y]}, {x, -Pi, Pi,Pi/10}, {y,-Pi,Pi, Pi/10}], 1]; You can use the function Point along with VertexColors . Now the points are places using the first three elements and the color is determined by the fourth. In this case I used Hue, but you can use whatever you prefer. Graphics3D[ Point[data[[All, 1 ;; 3]], VertexColors -> Hue /@ data[[All, 4]]], Axes -> True, BoxRatios -> {1, 1, 1/GoldenRatio}]

plotting - Mathematica: 3D plot based on combined 2D graphs

I have several sigmoidal fits to 3 different datasets, with mean fit predictions plus the 95% confidence limits (not symmetrical around the mean) and the actual data. I would now like to show these different 2D plots projected in 3D as in but then using proper perspective. In the link here they give some solutions to combine the plots using isometric perspective, but I would like to use proper 3 point perspective. Any thoughts? Also any way to show the mean points per time point for each series plus or minus the standard error on the mean would be cool too, either using points+vertical bars, or using spheres plus tubes. Below are some test data and the fit function I am using. Note that I am working on a logit(proportion) scale and that the final vertical scale is Log10(percentage). (* some test data *) data = Table[Null, {i, 4}]; data[[1]] = {{1, -5.8}, {2, -5.4}, {3, -0.8}, {4, -0.2}, {5, 4.6}, {1, -6.4}, {2, -5.6}, {3, -0.7}, {4, 0.04}, {5, 1.0}, {1, -6.8}, {2, -4.7}, {3, -1.