How can I make sure that Matlab mcc doesn't build incomplete stand-alone executables? - matlab

we use a scripting environment to automatically build, run and verify a stand-alone executable. We are working with Matlab R2010a x64. The Matlab compiler mcc is called from the Windows command line building a stand-alone application:
mcc -m -v -w enable -I source_folder -I common_folder -a specific_files_we_need our_program
The program consists of about 25 modules (.m files) and is using about 5 toolboxes. This is working fine as long as the correct license is available. mcc checks for a compiler license available, resolves dependencies, packs everything in the executable.
However if the license does not include the required toolboxes, mcc does not issue any warning or error. It builds the executable without the toolboxes. So the executable starts, seems to run at a first glance but crashes if a line of code requiring a toolbox is reached.
I am expecting from a compiler that it informs me about missing components. What can I do to get informed about missing components? How can I make sure that mcc does not put together incomplete executables? Am I missing something in the call to mcc?
Preferably I would like to setup the compiling in a way that it stops if things are missing.
\Zweikeks

The simplest way is in your compilation script you can checkout the licenses required, i.e.
license('checkout','Compiler')
license('checkout','control_toolbox')
You just add the 5 toolboxes that need to be checked out -> if the license function cant checkout the license it returns false which you can then use to abort the compilation.

This is what I finally came up with:
I can check before compilation which toolboxes are needed and call license() accordingly. Or I can implement a built-in check into the executable itself. (Called with a special parameter after compilation triggers a self-check for available toolboxes.) In either case I need the names of the required toolboxes.
I tried several ways to generate a list of toolboxes. In short: Running the program in Matlab and then entering license('inuse') is not very reliable. depfun() descends way to much. mydepfun() and fdep() do not descend enough.
I think the problem with mydepfun() and fdep() is that they do not descend into the \toolbox\shared folder. So I took mydepfun() from Tobias Kienzler (link to the original sources) and modified it:
function [list,callers,tboxes_found] = i_scan(f)
func = i_function_name(f);
[list,~,~,~,~,~,callers,~] = depfun(func,'-toponly','-quiet');
toolboxroot = fullfile(matlabroot,'toolbox');
sharedroot = strcat(toolboxroot, filesep, 'shared');
intoolbox = strncmpi(list,toolboxroot,numel(toolboxroot));
inshared = strncmpi(list,sharedroot, numel(sharedroot));
tboxes_found = list(intoolbox & ~inshared);
tboxes_found = regexpi(tboxes_found, '[\\/]toolbox[\\/](.+?)[\\/]', 'tokens');
tboxes_found = cellfun(#(cfun) cfun{1}, tboxes_found);
list = list(~intoolbox | inshared);
callers = callers(~intoolbox | inshared);
for jj = 1:numel(list)
c = callers{jj};
cs = cell(numel(c),1);
for kk = 1:numel(c)
cs{kk} = list{c(kk)};
end;
callers{jj} = cs;
end;
This way i_scan(f) is returning the toolboxes and also descends into \toolbox\shared. The main function of mydepfun() just collects the toolboxes:
function [filelist,callers,toolboxes] = mydepfun(fn,recursive)
.
.
toolboxes = {};
[filelist,callers,tboxes_found] = i_scan(foundfile);
toolboxes = [toolboxes; tboxes_found];
.
.
[newlist,newcallers,tboxes_found] = i_scan(toscan{1});
toolboxes = [toolboxes; tboxes_found];
.
.
toolboxes = unique(toolboxes);
The toolboxes listed are the ones our source code uses. The modified mydepfun() seems to work fine. (Apart from the typical problems caused by elements only resolved during run time like eval(), function handles, callbacks etc.)
And: The dependeny walkers I have seen - like mydepfun() - are using depfun() inside. depfun() is not reliable since it silently ignores all source code not on the path (also its return prob_files is empty in this case). So care has to be taken that the Matlab path is set correctly. (Also any additional pathes are problematic since Matlab may take unexpectedly functions with the same name from other locations.)
After all, I think, this is a good way to make my build process more reliable.
/Zweikeks

I just got another hint from the Mathworks forum. The compiler writes out mccExludedFiles.log. This is listing missing toolboxes.
For example
mccExludedFiles.log:
C:\Program Files\MATLAB\R2010a\toolbox\shared\optimlib\fmincon.m
called by ...c:\temp\whatever\source\code.m
(because the required licenses are not available.)
(Other missing files in the source code do not get listed, though.)
/Zweikeks

Related

Referencing External Files in JModelica

I have a Modelica file that references c code during simulation through an external library *.a file.
For example:
model CallAdd
input Real FirstInput(start=0);
input Real SecondInput(start=0);
output Real FMUOutput(start=0);
function CAdd
input Real x(start=0);
input Real y(start=0);
output Real z(start=0);
external "C" annotation(Library = "CAdd", LibraryDirectory = "modelica://CallAdd");
end CAdd;
equation
FMUOutput = CAdd(FirstInput,SecondInput);
annotation(uses(Modelica(version = "3.2.1")));
end CallAdd;
When opening the Modelica model in OpenModelica the required files appear to be automatically loaded because it simulates and gives appropriate results.
However, when I try to compile the Modelica file with JModelica-SDK-1.12 I receive an error that the library *.a file could not be found.
So my question is: What is the proper way to reference additional files when using compile_fmu in JModelica?
With no success, I've tried:
# Import the compiler function
from pymodelica import compile_fmu
model_name = "CallAdd"
mo_file = "CallAdd.mo"
# Compile the model and save the return argument, for use later if wanted
my_fmu = compile_fmu(model_name, mo_file, target="cs",compiler_options = {'extra_lib_dirs':'C:/ToFolderContainingLib/'})
The strange thing is that when I was using JModelica-1.17 (non-SDK) the file compiled fine but the results didn't make sense. I was recommended to try the SDK version to see if it fixed my errors in my previous post here.
Try positioning the external library in sub-folder named as the platform your currently on. So in your example, I'd position the library (libCAdd.a) in sub-folder named linux64, as I'm on a 64bit Linux machine and then run the code.
If is a small piece of C code, as a last alternative you could try to include the C file directly in the Modelica code:
external "C" annotation(Include="
// the entire C code here
");
Hopefully the JModelica people will give you a better answer soon.
You could try to ask this on their website also:
http://www.jmodelica.org/forum

Function to convert relative paths to absolute paths?

I've spent quite some time to no avail looking for a built-in MATLAB function to convert relative file paths to absolute file paths (portably).
Is there one?
I'm looking for something preferably "built-in" (i.e. available somewhere in the MATLAB distribution, including one of its toolboxes). Even a "package-private" function would be acceptable, as long as I can examine the source code of function. Second best would be a third-party function, as long as it comes with a decent test suite. I am not looking for a function written in response to this question.1
Absent any of the above, even a function to test (portably) whether a path is absolute or not would do (with the same conditions as before: either a "built-in" function or a third-party function with a test suite).
1 The difficulty with implementing such a function is not writing the function itself, but rather writing a sufficiently complete test suite for it (and, of course, making sure that the function passes all the tests!).
fullfile(pwd, relative_path) converts a relative to a absolute path.
You can test if a path is absolute using
javaFileObj = java.io.File(pathToBeTested);
javaFileObj.isAbsolute()
Unlike char(javaFileObj.getCanonicalPath()), which indeed sometimes incorrectly returns a non-existent path relative to C:\Windows\System32, isAbsolute() seems to work properly (tested on Win7, MATLAB 2015b) Therefore the code for constructing the absolute path would look like
function F = rel2abs(F)
if ~java.io.File(F).isAbsolute
F = fullfile(pwd,F);
end
This function has the advantage of being idempotent.
The fullfile(pwd, relative_path) hack works well for me, but if you want something to get you the canonical form, there is no built-in (as of 2015b), but there is a well regarded downloadable script.
http://www.mathworks.com/matlabcentral/fileexchange/28249-getfullpath
See if which fulfills your requirements:
full_path = which(relative_path);
OK, let's resurrect an old thread, if anyone is looking for this.
Here is a method, if you want the absolute path relative to your current working directory (or pwd).
% pwd: 'C:\first\branch'
dir('.\').folder % returns same as pwd, 'C:\first\branch'
dir('..\').folder % returns path one level up from pwd, 'C:\first'
dir('..\parallel_branch').folder % returns the absolute path to "parallel_branch" folder next to pwd, 'C:\first\parallel_branch'
Here's the method that MathWorks themselves uses:
[status, info] = fileattrib(file);
if status
% Return the full path if fileattrib found the file.
fullFilePath = info.Name;
end
For someone landing here with slightly relaxed portability requirements (or a suitable test bench), another idea is to create a folder listing containing only a single file using the built-in dir function, followed by path concatenation using fullfile.
dirListing = dir(relPath);
absPath = fullfile(dirListing(1).folder, dirListing(1).name);
Pros:
Idempotent, i.e. rel2abs(rel2abs(path)) = rel2abs(path)
Simplifies out any /. and /.. in the relative path
Cons:
This only works if the file exists
Tested using Matlab R2020a on Windows

How to reliably detect os/platform in Go

Here's what I'm currently using, which I think gets the job done, but there's got to be a better way:
func isWindows() bool {
return os.PathSeparator == '\\' && os.PathListSeparator == ';'
}
As you can see, in my case all I need to know is how to detect windows but I'd like to know the way to detect any platform/os.
Play:
http://play.golang.org/p/r4lYWDJDxL
Detection at compile time
If you're doing this to have different implementations depending on the OS, it is more useful to
have separate files with the implementation of that feature and add build tags to each
of the files. This is used in many places in the standard library, for example in the os package.
These so-called "Build constraints" or "Build tags" are explained here.
Say you have the constant PATH_SEPARATOR and you want that platform-dependent, you
would make two files, one for Windows and one for the (UNIX) rest:
/project/path_windows.go
/project/path_unix.go
The code of these files would then be:
path_windows.go
// +build windows
package project
const PATH_SEPARATOR = '\\'
path_unix.go
// +build !windows
package project
const PATH_SEPARATOR = '/'
You can now access PATH_SEPARATOR in your code and have it platform dependant.
Detection at runtime
If you want to determine the operating system at runtime, use the runtime.GOOS
variable:
if runtime.GOOS == "windows" {
fmt.Println("Hello from Windows")
}
While this is compiled into the runtime and therefore ignores the environment,
you can nevertheless be relatively certain that the value is correct.
The reason for this is that every platform that is worth distinguishing needs
rebuilding due to different executable formats and thus has a new GOOS value.
Have you looked at the runtime package? It has a GOOS const: http://golang.org/pkg/runtime/#pkg-constants
It's 2022 and the correct answer for go 1.18+ is:
At runtime you want:
if runtime.GOOS == "windows" {
// windows specific code here...
}
If you need to determine the filesystem path separator character
Use: os.PathSeparator
Examples:
c:\program files
/usr/local/bin
If you need the Path List separator as used by the PATH environment variable
Use: os.PathListSeparator
Examples:
/usr/local/bin:/usr/local:
"C:\windows";"c:\windows\system32";
Since this is an older question and answer I have found another solution.
You could simply use the constants defined in the os package. This const returns a rune so you would need to use string conversion also.
string(os.PathSeparator)
string(os.PathListSeparator)
Example: https://play.golang.org/p/g6jnF7W5_pJ
I just stumbled on this looking for something else and noticed the age of this post so I'll add a more updated addition. If you're just trying to handle the correct filepath I would use filepath.Join(). Its takes all of the guesswork out of os issues. If there is more you need, other than just filepath, using the runtime constants (runtime.GOOS & runtime.GOARCH) are the way to go: playground example
I tested in Go 1.17.1 which really worked for me.
package main
import (
"fmt"
"runtime"
)
func main(){
fmt.Println(runtime.GOOS)
}
Output:
darwin
With regards to detecting the platform, you can use Distribution Detector project to detect the Linux distribution being run.
The first answer from #nemo is the most apropiate, i just wanted to point out that if you are currently a user of gopls language server the build tags may not work as intended.
There's no solution or workaround up to now, the most you can do is change your editor's lsp configs (vscode, neovim, emacs, etc) to select a build tag in order to being able to edit the files with that tag without errors.
Editing files with another tag will not work, and trying to select multiple tags fails as well.
This is the current progress of the issue github#go/x/tools/gopls

How to get scons to treat a directory itself as a target?

I'm trying to set up a build involving an external tool which produces a directory as output (doxyindexer for the curious). So far, I've essentially got these commands:
target = "doxysearch.db/iamchert"
doxygen.Doxyindexer(target,["project1.xml","project2.xml","project3.xml"])
Default([target])
Default(Install(ARGUMENTS["cgibin"],"doxysearch.db"))
The problem that I'm having is that I think I'd like target to be the directory itself, not some random file inside the directory. There's nothing I can glob because the target doesn't exist until I build it and I don't want to presume anything that Dimitri might change! When I use the directory as the target, I get this error:
TypeError: Tried to lookup Dir 'doxysearch.db' as a File.:
which is why I picked iamchert to be the target. Those lines all seem to work as expected, even if my approach is a hack. However, I can't get that last line to work. I need to copy the directory doxysearch.db into the cgi-bin directory, which is specified on the command line by the user. Maybe someone can explain how to do this step properly? I'm a newb when it comes to scons!
I'm having trouble googling the answer because all the search words involved are too common to find me specific help!
SCons does in fact treat all the files in a dir as dependencies of that dir. There are some dark corners that need work, but it should work in a simple case like this.
What you need is the undocumented target_factory builder flag. When you define Doxyindexer do it like this:
doxyindexer = Builder(..., target_factory=env.fs.Dir)
and have your builder return the dir itself. That should avoid the TypeError you were getting.
Im not sure how well SCons will work with the target being a directory. The issue is: How should SCons determine if the directory has changed or not to know if it should be built? The obvious answer would be that a directory is considered to be changed if it has more or less files therein, but I dont think SCons currently does this check and you might have to make your own builder to get it.
I did the following example to test this, and it never builds:
env = Environment()
env.Command(target = 'targetDir',
source = 'srcTextFile',
action = Copy("$TARGET", "$SOURCE"))
When I execute SCons, I always get the same result:
scons: '.' is up to date
Regarding your SCons code, I think it would work better as follows:
targetDir = "doxysearch.db/iamchert"
srcFiles = ["project1.xml","project2.xml","project3.xml"]
doxygenTarget = doxygen.Doxyindexer(targetDir, srcFiles)
    # This may need to be called via the Command() builder like this:
    # cmd = "doxygen.Doxyindexer("$TARGET", "$SOURCE")
    # doxygenTarget = env.Command(target=targetDir, source=srcFiles, action=cmd)
# This call to Default isnt really necessary
Default(doxygenTarget)
Install(ARGUMENTS["cgibin"], doxygenTarget)

PATH specification when using the R package gdata

Problem: helpers fail when reading an .xls into R using gdata
I have some .xls that i'd like to read into R. I am able to do so using read.xls in the gdata package, however the helper functions sheetNames and sheetCount fail - i'd like to understand what i'm doing wrong so that i can use them as they would be very useful.
require(gdata)
fp <- file.path('~/data/first.xls')
When i know the structure of the sheets, and am able to point it at my intended data sheet, the perl script runs just fine:
firstdata <- read.xls(fp, sheet=2)
and i have my data... in firstdata.
However, in the very same sheets, the helpers fail.
I find myself opening the .xls in excel, figuring them out, and then loading into R using read.xls(fp, sheet=N) -- not a disaster, but neither is it ideal.
In particular, when the sheets are not my own, and I need information about them before i can set sheet=N in read.xls(), the helper functions sheetNames and sheetCount would be very useful, however they fail -- why?
sheetCount(fp)
> sheetNames(fp)
Error in read.table(tc, as.is = TRUE, header = FALSE) :
no lines available in input
In addition: Warning message:
running command ''/usr/bin/perl' '~/R/wd/raRpackages/gdata/perl/sheetNames.pl' '~/data/first.xls'' had status 2
Unable to open file '~/data/first.xls'.
and:
> sheetCount(fp)
Error in read.table(tc, as.is = TRUE, header = FALSE) :
no lines available in input
In addition: Warning message:
running command ''/usr/bin/perl' '~/R/wd/raRpackages/gdata/perl/sheetCount.pl' '~/data/first.xls'' had status 2
Unable to open file '~/data/first.xls'.
After a bit of fiddling, i (quite by accident) found that using the full path solves this problem:
fp2 <- file.path("/Users/ricardo/data/first.xls")
sheetcount(fp2)
[1] 13
It looks like ~ isn't being expanded out to your home directory. This expansion is usually done by the shell so R probably won't do it and perl definitely won't.
Instead, use the explicit path or $HOME or $ENV{HOME} from within the Perl program.