Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide ModelicaUtilities.h file for people writing their own C external function #4484

Open
casella opened this issue Oct 21, 2024 · 37 comments
Assignees
Milestone

Comments

@casella
Copy link
Contributor

casella commented Oct 21, 2024

In #3867 there was a discussion about whether ModelicaUtilities.h in the MSL was meant to be tool-specific, which ended up in the removal of that file from the MSL.

If I understand correctly, the key argument for that made by @henrikt-ma:

While it is fine for a library to need this file, each library should not contain the file. The file should be provided by the Modelica tool environment, which should provide a file corresponding to the currently active language version.

In my opinion, the file should be removed from the MSL to make this clear, and the issue should stay in this repository.

Unfortunately, this leaves the following scenario without a clear solution. Suppose that I am writing a library (not the MSL, whose binaries are compiled by each tool vendor) that is meant to be usable by multiple tools (ca va sans dire), uses external functions, and is released with pre-compiled binaries (DLL/shared libraries). When I am writing the C code of external functions that need to call ModelicaUtilities stuff, I need to #include "ModelicaUtilities.h" to actually compile the code.

The question is, where do I take that ModelicaUtilities.h file? Currently, I only see two feasible options:

  1. I take it a specific tool version from the tool's installation directory
  2. I write ModelicaUtilities.h myself, based on what I read in Section 12.9.6

Option 1. is obviously dangerous from a portability point of view: what guarantee do I have that the resulting binary libraries are compatible with all Modelica tools? In fact, I may not even be allowed to do that by licencing agreements.
Option 2. is straightforward, but it seems a bit odd that we are not providing those definitions arlready packaged in a ModelicaUtilitites.h file somewhere.

I think we should provide a version of ModelicaUtilities.h that only contains the function declarations (the interfaces / fingerprints), making it clear in the file header that this is not meant to be used by Modelica tools to manage their runtimes, but only to compile external functions in a tool-independent way.

Technically speaking, this is part of the Modelica Language Specification. I guess that putting it in the MSL makes sense, provided we explain clearly what the purpose is.

Keeping @fedetftpolimi in the loop.

@casella casella added this to the MSL4.1.0 milestone Oct 21, 2024
@casella casella self-assigned this Oct 21, 2024
@henrikt-ma
Copy link
Contributor

henrikt-ma commented Oct 21, 2024

One way to deal with the dependency on the MLS version would be to include some sort of assert that will fail unless a (MODELICA_LANGUAGE_MAJOR, MODELICA_LANGUAGE_MINOR) tuple has been set to values matching exactly what is in the ModelicaUtilities.h. I don't know exactly how to do this in a C header, but expect that there will be some way…

If we solve that part, we "only" need to agree upon what a correct content of ModelicaUtilities.h should look like. Once agreed, we should stop thinking of this file as a tool-specific file, and it should be expected (and probably even defined in the specification) that the file as provided by one tool should work with any other tool.

At this point, I don't see why there should be any differences at all between tools, and it would be natural to include the file with every library, including ModelicaServices. The file included with ModelicaServices would then naturally become the de-facto standard file for tools makers and library authors to copy into their products.

@henrikt-ma
Copy link
Contributor

Option 1. is obviously dangerous from a portability point of view: what guarantee do I have that the resulting binary libraries are compatible with all Modelica tools? In fact, I may not even be allowed to do that by licencing agreements.

The central question here is why any tool would depend on ModelicaUtilities.h content that would not work with different tools?

@beutlich
Copy link
Member

In #3867 we agreed that ModelicaUtilities.h is tool-specific, right? In that case it is no more applicable to compile external objects/functions with one tool-specific ModelicaUtilities.h and re-use the binary in different simulation tools.

I also consider the removal of ModelicaUtilities.h as a breaking change in the sense that it should force a v5.0.0 instead of v4.1.0.

@beutlich
Copy link
Member

The central question here is why any tool would depend on ModelicaUtilities.h content that would not work with different tools?

One scenario is that tools might not yet fully support all utility functions of MLS 3.6, i.e. the later introduced functions ModelicaWarning or ModelicaDuplicateString.

@fedetftpolimi
Copy link

Making ModelicaUtilities.h tool-specific is a massive pain for library developers, if that decision was made I strongly suggest it should be un-done asap.

Case in point, https://github.com/modelica-3rdparty/ExternalMedia
It is a library that is distributed with pre-built shared objects for Windows, Linux and Mac. We currently link those shared objects against a ModelicaUtilities.h that I think is an old version of the one provided with the MSL: https://github.com/modelica-3rdparty/ExternalMedia/blob/master/Projects/Sources/ModelicaUtilities.h

It would be unreasonable to have to recompile and distribute ExternalMedia with a different ModelicaUtilities.h for every OS and tool combination, also considering that:

  1. making ModelicaUtilities.h tool-specific means tool vendors are free to alter its content at will from one tool release to the other, what should ExternalMedia do, keep track of those changes and compile a different version of the shared library for every OS and tool and tool version?
  2. new tool vendors could enter the market, what should ExternalMedia do, keep track of all Modelica tool vendors?

Unless ModelicaUtilities.h is standardized, the external functions interface is essentially broken and portability basically works "by chance" relying on undefined behavior. This needs to change.

@fedetftpolimi
Copy link

A tool that does not yet support all utility functions can provide the missing ones as stubs printing a message to the user "This feature is currently unsupported" or similar.

beutlich added a commit to beutlich/ModelicaStandardLibrary that referenced this issue Oct 21, 2024
Revert "refs modelica#3867: Remove LICENSE_ModelicaUtilities.txt"

This reverts commit 7fe506f.

Revert "refs modelica#3867: Remove ModelicaUtilities.h"

This reverts commit 1bb1d40.
beutlich added a commit to beutlich/ModelicaStandardLibrary that referenced this issue Oct 21, 2024
Revert "refs modelica#3867: Remove LICENSE_ModelicaUtilities.txt"

This reverts commit 7fe506f.

Revert "refs modelica#3867: Remove ModelicaUtilities.h"

This reverts commit 1bb1d40.
beutlich added a commit to beutlich/ModelicaStandardLibrary that referenced this issue Oct 21, 2024
Revert "refs modelica#3867: Remove LICENSE_ModelicaUtilities.txt"

This reverts commit 7fe506f.

Revert "refs modelica#3867: Remove ModelicaUtilities.h"

This reverts commit 1bb1d40.
@beutlich
Copy link
Member

I am also in favour of reverting #3871 for MSL v4.1.0 and provided a (draft) PR in #4487.

@casella
Copy link
Contributor Author

casella commented Oct 21, 2024

The central question here is why any tool would depend on ModelicaUtilities.h content that would not work with different tools?

Excerpt from OpenModelica's ModelicaUtilities.h:

void ModelicaFormatMessage(const char *string, ...) MODELICA_FORMATATTR_PRINTF;
/*
Output the message under the same format control as the C-function printf.
*/

void ModelicaVFormatMessage(const char *string, va_list args) MODELICA_FORMATATTR_VPRINTF;
/*
Output the message under the same format control as the C-function vprintf.
*/

MODELICA_NORETURN void ModelicaError(const char *string) MODELICA_NORETURNATTR;
/*
Output the error message string (no format control). This function
never returns to the calling function, but handles the error
similarly to an assert in the Modelica code.
*/

Excerpt from Dymola's ModelicaUtilities.h:

DYMOLA_STATIC void ModelicaFormatMessage(const char *string, ...) MODELICA_FORMATATTR_PRINTF;
/*
Output the message under the same format control as the C-function printf.
*/

DYMOLA_STATIC void ModelicaVFormatMessage(const char *string, va_list args) MODELICA_FORMATATTR_VPRINTF;
/*
Output the message under the same format control as the C-function vprintf.
*/

MODELICA_NORETURN DYMOLA_STATIC void ModelicaError(const char *string) MODELICA_NORETURNATTR;
/*
Output the error message string (no format control). This function
never returns to the calling function, but handles the error
similarly to an assert in the Modelica code.
*/

As I understand (I'm not the expert here, I might be wrong), the tool-specific macros are needed when compiling the simulation executable with a given tool, which is OK.

However, if I am writing a library with external functions that needs to call such utility functions, I obviously don't want to use any such tool-specific .h file in its source code, because it may not work with other tools. I just need a Modelica.Utilities.h header that contains the headers as described in the MLS Section 12.9.6:

void ModelicaMessage (const char* string);
void ModelicaWarning (const char* string);
void ModelicaError(const char* string);
void ModelicaFormatMessage (const char* format , ...);
void ModelicaFormatWarning (const char* format , ...);
void ModelicaFormatError (const char* format , ...);
void ModelicaVFormatMessage (const char* format , va_list ap);
void ModelicaVFormatWarning (const char* format , va_list ap);
void ModelicaVFormatError (const char* format , va_list ap);
char* ModelicaAllocateString (size_t len);
char* ModelicaAllocateStringWithErrorReturn (size_t len);
char* ModelicaDuplicateString (const char* str );
char* ModelicaDuplicateStringWithErrorReturn (const char* str );

That's the public interface of those functions defined by the MLS, so if I use this for my external functions, I expect them to work in all tools.

beutlich added a commit to beutlich/ModelicaStandardLibrary that referenced this issue Dec 28, 2024
Revert "refs modelica#3867: Remove LICENSE_ModelicaUtilities.txt"

This reverts commit 7fe506f.

Revert "refs modelica#3867: Remove ModelicaUtilities.h"

This reverts commit 1bb1d40.
beutlich added a commit to beutlich/ModelicaStandardLibrary that referenced this issue Feb 1, 2025
Revert "refs modelica#3867: Remove LICENSE_ModelicaUtilities.txt"

This reverts commit 7fe506f.

Revert "refs modelica#3867: Remove ModelicaUtilities.h"

This reverts commit 1bb1d40.
@casella
Copy link
Contributor Author

casella commented Feb 11, 2025

Last call on this issue before we go ahead with the 4.1.0 release. As stated in the MLS Section 12.9.6:

This section describes the utility functions declared in ModelicaUtilities.h, which can be called in external Modelica functions written in C.

If I am writing my own external functions that need to call functions from ModelicaUtilities, I need to #include such a ModelicaUtilities.h header file in my source C code, otherwise it won't be valid. Note that if I want to pre-compile those binaries and ship them with the library I cannot use the tool-specific ModelicaUtilities.h header file provided by any specific Modelica tool I may have installed on my computer, since those external functions are expected to work with any Modelica tool, not just the installed one(s). As shown above, tool-specific ModelicaUtilities.h files often include tool-specific macros.

For clarity, please note that this ModelicaUtilities.h will not be the header file used by tool vendors to compile their own version of the ModelicaUtilities binaries. Each tool vendor will craft their own. This header file is meant to be used by everybody else, who needs to write Modelica functions that call the functions defined in such binaries.

I would suggest that we include a bare-bones ModelicaUtilities.h header file containing the following code only, which I took from the said Section of the MLS.

void ModelicaMessage (const char* string);
void ModelicaWarning (const char* string);
void ModelicaError(const char* string);
void ModelicaFormatMessage (const char* format , ...);
void ModelicaFormatWarning (const char* format , ...);
void ModelicaFormatError (const char* format , ...);
void ModelicaVFormatMessage (const char* format , va_list ap);
void ModelicaVFormatWarning (const char* format , va_list ap);
void ModelicaVFormatError (const char* format , va_list ap);
char* ModelicaAllocateString (size_t len);
char* ModelicaAllocateStringWithErrorReturn (size_t len);
char* ModelicaDuplicateString (const char* str );
char* ModelicaDuplicateStringWithErrorReturn (const char* str );

Two questions for the compiler experts, which I am not:

  • Q1: are the function signatures shown above good enough to compile my own external functions so that they work in all tools?

If the answer is YES, then we should add such a header file to the MSL, since I see no other reasonable place where to put it.

If the answer is NO, then the next question is:

  • Q2: what function signatures should I use to ensure that my external functions will work in all Modelica-compliant tools?

If for some reason it was not possible to have tool-neutral function signatures for the ModelicaUtilities functions, then I guess we'd have a big problem: basically there would be no such thing as tool-independent Modelica libraries as soon as they use external C code.

@HansOlsson, @henrikt-ma, @adrpo, @gkurzbach, can you please comment on that?

@HansOlsson
Copy link
Contributor

The answer depends on how freely tools have used the idea to provide their own version:

  • If the functions are just declared as above (including the extras) it should work as far I know. (I don't know if source code FMUs would cause complications for this.)
  • If the functions are declared using different calling conventions, or dll-import directives then there is no standard header that can automatically handle it. But, as described in Provide functions in ModelicaUtilities.h to external objects implemented as shared libraries #4476 (comment) you can still build a ExternalLibrary independently of the tool - it would require a special "ModelicaUtilities.h" (completely different from the one above) and some magic for each exposed function (can be hidden as a macro).

I don't know if we need the second option.

However, there can also be other issues in external libraries, e.g., using C++ code and throwing exceptions as if that was safe in C-interfaces (it isn't).

@beutlich
Copy link
Member

I am also in favour of reverting #3871 for MSL v4.1.0 and provided a (draft) PR in #4487.

I did not change my mind. Let's revert that PR and keep the compatibility. We still can clarify for any later MSL release. I can not accept a replacement of the proper developed ModelicaUtilities.h by such a poor stub ModelicaUtilities.h.

@maltelenz
Copy link
Contributor

In my opinion we are way (way way way) past the point where we make non-critical bug-fix changes for 4.1.0. I would not call this a critical bug-fix, since the library works just fine, and this is a question about developing other libraries.

@HansOlsson
Copy link
Contributor

Oh, a slight update of my answer. Some attributes of function declarations may in fact cause weird incompatibilities - specifically nonnull-attributes for pointers (as added in #1436 ) when using gcc/clang if the tool-vendor headers have attributes, and they don't compile their tool-libraries with -fno-delete-null-pointer-checks, and the external library is compiled differently. (Unless the compiler writers have fixed that mess recently.)

@beutlich
Copy link
Member

beutlich commented Feb 12, 2025

since the library works just fine

Well, but not out of the box. See https://github.com/modelica/ModelicaStandardLibrary/blob/maint/4.1.x/Modelica/Resources/BuildProjects/readme.txt#L28-L30.

As we also provide the binaries (as part of the released MSL) we need to get ModelicaUtilities.h from somewehere. Currently I take it from .CI/Test/ModelicaUtilities.h (https://github.com/modelica/ModelicaStandardLibrary/blob/maint/4.1.x/.CI/Test/ModelicaUtilities.h) which seems wrong.

If we remove the binaries completely (and not even provide in a released version) then we could get rid of ModelicaUtilities.h. But that's not scheduled for v4.1.0. Hence, my preference is to restore the ModelicaUtilites.h and keep the compatbility.

@maltelenz
Copy link
Contributor

As we also provide the binaries (as part of the released MSL) we need to get ModelicaUtilities.h from somewehere. Currently I take it from .CI/Test/ModelicaUtilities.h (https://github.com/modelica/ModelicaStandardLibrary/blob/maint/4.1.x/.CI/Test/ModelicaUtilities.h) which seems wrong.

Could you elaborate why that seems wrong to you?

To me, it seems good to have it somewhere that is not shipped with the library to customers (they don't need it), but still in the repo (the library developers need it to build binaries for a release).

@casella
Copy link
Contributor Author

casella commented Feb 13, 2025

Before we continue the discussion, let me write some statements that we should all agree upon:

  1. Modelica is an open standard: if I write Modelica code according to the standard, it is expected to run out of the box in every Modelica tool.
  2. Modelica allows to use external functions written in C, see MLS Section 12.9.
  3. According to MLS Section 12.9.4, the external function implementation can be provided by a compiled C library, which is referenced by the annotation(Library="library_name").
  4. Since different operating systems have different file formats for compiled libraries, the standard allows to provide compiled libraries for each of them. It is the library author's responsibility to compile the binaries for all the operating systems that may be used by the library users. All this is completely orthogonal to which specific tool is used to run the Modelica code
  5. According to MLS Section 12.9.6, there are utility functions declared in ModelicaUtilities.h which can be called in external Modelica functions written in C.

Please comment here if you believe that some of these statements are inaccurate.

@casella
Copy link
Contributor Author

casella commented Feb 13, 2025

Now, consider the following scenario. John Doe is writing a Modelica package with external C functions that he is also developing from scratch; he wants to provided them to the package users by a compiled C library. Of course he wants the package to be usable on all Modelica tools. He is not affiliated with any tool vendor, so he cannot ask tool vendors to compiler the libraries for him.

The question is: what ModelicaUtilites.h should John Doe include in the source code of his external C functions, so that once he's compiled them in a library, they will work in all tools?

  1. If we take the "Modelica is an open standard" statement seriously, there should be one such ModelicaUtilities.h file that works with all tools. We could use #ifdef macros to handle OS-specific stuff, e.g. DLLexport for Windows.
  2. I would find it extremely weird that the MA association does not provide such a file, and leaves John Doe on his own to figure out, particularly as this seems a non-trivial issue
  3. Although there could be different opinions on that, IMHO, the best place to do that is the Modelica Standard Library. That's were people are looking for, you know, the standard stuff. Where else?

@casella
Copy link
Contributor Author

casella commented Feb 13, 2025

I am also in favour of reverting #3871 for MSL v4.1.0 and provided a (draft) PR in #4487.

I did not change my mind. Let's revert that PR and keep the compatibility. We still can clarify for any later MSL release. I can not accept a replacement of the proper developed ModelicaUtilities.h by such a poor stub ModelicaUtilities.h.

I agree with that. We should carefully avoid to pass the message that the is no unique, tool-independent ModelicaUtilities.h header file, to be used by the John Does of the world.

@casella
Copy link
Contributor Author

casella commented Feb 13, 2025

In my opinion we are way (way way way) past the point where we make non-critical bug-fix changes for 4.1.0. I would not call this a critical bug-fix, since the library works just fine, and this is a question about developing other libraries.

I respectfully disagree. Removing that file in #3871 had consequences that IMHO were not considered carefully enough. We're not talking about a bug fix, we are talking about something more fundamental. It's never too late for that.

@casella
Copy link
Contributor Author

casella commented Feb 13, 2025

If we remove the binaries completely (and not even provide in a released version) then we could get rid of ModelicaUtilities.h. But that's not scheduled for v4.1.0. Hence, my preference is to restore the ModelicaUtilites.h and keep the compatbility.

@beutlich I hope my last comments clarify this point. ModelicaUtilities.h is not primarily provided for the tool vendors. The tool vendors can deal with that, no problem. ModelicaUtilities.h is provided for John Doe so he can actually implement his own Modelica library according to the standard. He would be probably clueless otherwise.

The fact that MSL libraries that are meant to be compiled by the tool vendors are present or not in the repo is totally irrelevant.

@fedetftpolimi
Copy link

@maltelenz

To me, it seems good to have it somewhere that is not shipped with the library to customers (they don't need it),

Customers do need it.

@HansOlsson

The answer depends on how freely tools have used the idea to provide their own version:

Let's upscale this statement to its absurd consequences.

Section 2.3.3 of the Modelica specification lists the Modelica language keywords. Among them there is the keyword model.
Let's assume a tool vendor decides to provide their own version of this keyword and call it modeeel. If you Modelica code uses model, it does not compile with that tool, but if you use modeeel, it does.
Should this tool be a compliant Modelica tool? Of course no, because there would be no way for users to write models and libraries that work with all tools.

Section 12.9.6 of the Modelica specification states that the ModelicaUtilities.h file should contain a function void ModelicaWarning(const char*). Let's assume a tool vendor decides to provide their own version of ModelicaUtilities.h where the ModelicaWarning function is declared with some added custom stuff so that it only works if you include the tool-provided ModelicaUtilities.h.
Should this tool be a compliant Modelica tool? Of course no, for the same reason above.

The fact that only a minority of Modelica users do write Modelica libraries which also contain C code does not make this fact less significant. Especially since some of these "low level" Modelica+C libraries are released to the public so that the wider Modelica community can benefit from them. ExternalMedia is an example.

Tool vendors should not be allowed to customize ModelicaUtilities.h. If some OS-specific details such as dllexport declarations are needed, they should be contributed by means of #ifdefs to the one and only ModelicaUtilities.h that is present in the MSL.

It may be a long way, but we should head in the direction in which no matter the tool, no matter if at compile-time or run-time, no matter what language the external code is written in, the functions declared in ModelicaUtilities.h should always work. Removing ModelicaUtilities.h from the MSL is a step in the wrong direction.

@casella
Copy link
Contributor Author

casella commented Feb 13, 2025

Tool vendors should not be allowed to customize ModelicaUtilities.h. If some OS-specific details such as dllexport declarations are needed, they should be contributed by means of #ifdefs to the one and only ModelicaUtilities.h that is present in the MSL.

The question is, what should ModelicaUtilities.h contain so that I can write #include "ModelicaUtilities.h" in my external C function code, and call its functions successfully?

I understand that the version of that file that we had in MSL 4.0.0 and that was removed by PR #3871 is what we need, or at least very close to it. So I'm definitely in favour of reverting #3878 and keep what we had in MSL 4.0.0. That's not a bug fix, it's a reversal of a questionable decision which was taken long time ago (when I was not responsible of MAP-Lib, BTW). We're still in time to reverse it, before causing potential future harm.

@fedetftpolimi, if tool vendors wants to customize ModelicaUtilities.h when compiling ModelicaUtilities.c inside their tools, e.g. by adding further pragmas or anything, that's none of our business. As long as it works well with the interface defined by the standard ModelicaUtilities.h mentioned above, tool vendors can do whatever they want.

@AHaumer
Copy link
Contributor

AHaumer commented Feb 13, 2025

I fully agree with @casella and others:
We have to have a standardized ModelicaUtilities.h included in the MSL.
If tool vendors prefer to customize that h-file, it's their (and their customers') problem.
I would even prefer to have a set of source code for these functions (not compiled - the standadrd allows to include source code).
That's what I understand by open standard and open for every tool.

@HansOlsson
Copy link
Contributor

Section 12.9.6 of the Modelica specification states that the ModelicaUtilities.h file should contain a function void ModelicaWarning(const char*). Let's assume a tool vendor decides to provide their own version of ModelicaUtilities.h where the ModelicaWarning function is declared with some added custom stuff so that it only works if you include the tool-provided ModelicaUtilities.h. Should this tool be a compliant Modelica tool? Of course no, for the same reason above.

I'm not disagreeing with making that explicit - I was just trying to answer the questions about consequences.

I also note that there's a subtle issue: ModelicaUtilities.h should (as a header file) containing function declarations, and that isn't clear at the moment (I will open a PR).

However, one of the previous issues was about different versions of ModelicaUtilities.h (when the language gets more stuff - if it isn't backwards compatible it becomes a lot messier) and relatedly how it should be distributed. As far I understand the issue isn't really that people need it for code in MSL, but that people who build libraries like ExternalMedia have MSL and need it. (Subtly different.)

One solution for all of that is to not place ModelicaUtilities.h in Modelica/Resources/Include - but somewhere in the repo, and document that. One possibility would be in ModelicaServices/Resources/Include.

Thus:

  • If you want to build a library using the header, you still have it, and I don't see external libraries including MSL-includes in general.
  • It can be used to build the libraries included with MSL since it is part of the repo.
  • It doesn't interfere with the ModelicaUtilities.h that tools provide (the latter could be for a later version with more functions), since it's not automatically included in the include-path. Note that it is fine to compile your external library with an earlier version (you will have a smaller interface).

The fact that only a minority of Modelica users do write Modelica libraries which also contain C code does not make this fact less significant. Especially since some of these "low level" Modelica+C libraries are released to the public so that the wider Modelica community can benefit from them. ExternalMedia is an example.

Tool vendors should not be allowed to customize ModelicaUtilities.h. If some OS-specific details such as dllexport declarations are needed, they should be contributed by means of #ifdefs to the one and only ModelicaUtilities.h that is present in the MSL.

Generally agree, but to be clear: Annotations helping code analyzers/warnings are not "customizing" it as they don't impact the actual function calls. So, the current one should be ok, whereas dllimporting it or modifying the calling convention is not.

@HansOlsson
Copy link
Contributor

The last point mean that I'm in favor of restoring the old ModelicaUtilities.h (somewhere in the repo), not adding the barebones one.

@HansOlsson
Copy link
Contributor

void ModelicaVFormatMessage(const char string, va_list args) MODELICA_FORMATATTR_VPRINTF;
/

Output the message under the same format control as the C-function vprintf.
*/

MODELICA_NORETURN void ModelicaError(const char string) MODELICA_NORETURNATTR;
/

Output the error message string (no format control). This function
never returns to the calling function, but handles the error
similarly to an assert in the Modelica code.
*/
As I understand (I'm not the expert here, I might be wrong), the tool-specific macros are needed when compiling the simulation executable with a given tool, which is OK.

No, they are never needed (the DYMOLA_STATIC is a bit different - it should certainly not be used when building a library.) However, they are a really good idea when compiling any code using these functions, as they document things and make it easy for the compiler to detect certain issues.

In particular:

  • ModelicaVFormatMessage behaves like vprintf, so the compiler can check that the first argument is a format string.
  • ModelicaError doesn't return, so any code following it will not run.

That is the documented behavior, so anyone using the functions can rely on those facts - it's just that it isn't (or wasn't) part of the C-standard, which mean that it has to be defined using various compiler extensions.

@HansOlsson
Copy link
Contributor

Oh, and I noticed that the old ModelicaUtilities.h was in Modelica/Resources/C-Sources, not Include as I thought (right?).
That also works, so just restoring it as in the proposed PR seems the simplest. Having it in ModelicaServices would have some downsides (e.g., tool vendors already replace the entire package).

@beutlich
Copy link
Member

beutlich commented Feb 14, 2025

Could you elaborate why that seems wrong to you?

The issue I see is that we (MAP-Lib) ship (production) binaries for MSL that are created with a fle Modelicautilities.h with unknown origin or rather that orginates from a folder specific for CI tests.

so just restoring it as in the proposed PR seems the simplest.

See #4487. Note that this PR targets maint/4..x branch (not master, where there is the intention to remove binaries from the repo).

beutlich added a commit to beutlich/ModelicaStandardLibrary that referenced this issue Feb 14, 2025
Revert "refs modelica#3867: Remove LICENSE_ModelicaUtilities.txt"

This reverts commit 7fe506f.

Revert "refs modelica#3867: Remove ModelicaUtilities.h"

This reverts commit 1bb1d40.
@fedetftpolimi
Copy link

@HansOlsson

Generally agree, but to be clear: Annotations helping code analyzers/warnings are not "customizing" it as they don't impact the actual function calls. So, the current one should be ok, whereas dllimporting it or modifying the calling convention is not.

I can't find a reason why adding annotations to help code analyzers would break compatibility across Modelica tools, so from my perspective they are fine and thus I agree to restore the old ModelicaUtilities.h and not adding the barebones one.

Do some tool vendor disagree? Let us know.

@HansOlsson
Copy link
Contributor

BTW: A minor note regarding no-return - it has been added to C23, so we could use something like this:

#undef MODELICA_NORETURN
#undef MODELICA_NORETURNATTR
#if __STDC_VERSION__ >= 202311L  || __cplusplus >= 201103L
/* Alternatively with messy gcc-tests as before */

#define MODELICA_NORETURN [[noreturn]]
#define MODELICA_NORETURNATTR

#elif __STDC_VERSION__ >= 201112L
#define MODELICA_NORETURN _Noreturn
#define MODELICA_NORETURNATTR
#elif defined(__clang__)
/* Encapsulated for Clang since GCC fails to process __has_attribute */
#if __has_attribute(noreturn)
#define MODELICA_NORETURN
#define MODELICA_NORETURNATTR __attribute__((noreturn))
#else
#define MODELICA_NORETURN
#define MODELICA_NORETURNATTR
#endif
#elif (defined(__GNUC__) && __GNUC__ >= 3) || \
      (defined(__GNUC__) && defined(__GNUC_MINOR__) && __GNUC__ == 2 && __GNUC_MINOR__ >= 8) || \
      (defined(__SUNPRO_C) && __SUNPRO_C >= 0x5110)
#define MODELICA_NORETURN
#define MODELICA_NORETURNATTR __attribute__((noreturn))
#elif (defined(_MSC_VER) && _MSC_VER >= 1200) || \
       defined(__BORLANDC__)
#define MODELICA_NORETURN __declspec(noreturn)
#define MODELICA_NORETURNATTR
#else
#define MODELICA_NORETURN
#define MODELICA_NORETURNATTR
#endif

@beutlich
Copy link
Member

A minor note regarding no-return - it has been added to C23

Yes, we should add them. I prefer to also keep the existing defintions as fallback since I usually build with older compilers.

@HansOlsson
Copy link
Contributor

HansOlsson commented Feb 14, 2025

A minor note regarding no-return - it has been added to C23

Yes, we should add them. I prefer to also keep the existing defintions as fallback since I usually build with older compilers.

I can understand that, but it seems weird at the moment.

The special gcc-handling for below 4.8 (using attribute((noreturn)) for versions [2.8, 4.8) and nothing below 2.8) is only applied if defines indicate that it is C++11 and later, even though C++11 added a standard variant. That is only relevant if the compiler was broken.

Digging deeper there seems to be such an issue with C++11 support in gcc 4.7.x, but neither in later versions nor in earlier ones. Later versions were corrected, and gcc 4.6 slightly predates C++11 and setting C++0x for that compiler doesn't seem to define __cplusplus to trigger this.

Note that without C11, C23, C++11 there's still a fall-back for gcc 2.8 and later.

And as far as I understand the latest gcc doesn't support the standard-attributes if specifying an old version of the standard, so I would assume the __attribute__((noreturn) fall-back is the correct one.

Added:
So basically changing the version test to ignore C++11 before gcc 4.8:

#if __STDC_VERSION__ >= 202311L  || (__cplusplus >= 201103L && ( !defined(__GNUC__) || __GNUC__ >= 5 || __GNUC__>=4 && __GNUC_MINOR__ >= 8) )
/* Was first correctly implemented for C++11 in gcc 4.8 */

@casella
Copy link
Contributor Author

casella commented Feb 14, 2025

I'm glad to see some consensus building 😃.

As I understand, there are potentially different ModelicaUtilities.h files, depending on the context.

  1. the header file used to actually compile the ModelicaUtilities C-library for a given tool, e.g. for its runtime binaries - this is under the responsibility of the tool vendor
  2. the header file used to compile MSL binaries, under the responsibility of MAP-Lib for 4.1.0, then we'll leave it to the tool vendors
  3. the header file needed by John Doe that wants to use external C functions calling ModelicaUtilities C functions in his libraries - this will always be under the responsibility of MAP-Lib, also for future versions of MSL

I would claim that the SSoT (besides the MLS Sect. 12.9.6, which is however "too bare-bones" to be usable) is 3., which sets the standard for all the John Does of the world. All other variants of that file need to be 100% compatible with it, possibly only adding some compiler optimizations or extra checks.

ModelicaUtilities 2. is a bit tricky. I scanned the whole MSL for "ModelicaUtilities.h" and I found it in two categories of places:

  • #include "ModelicaUtilities.h" macros in the source files ModelicaIO.c, ModelicaMatIO.c, ModelicaRandom.c, etc., which contain the implementations of all external functions of the MSL.
  • annotation(IncludeDirectory="modelica://Modelica/Resources/C-Sources", Include="#include \"ModelicaUtilities.h\""... in Modelica.Utilities.Streams.error, which just calls ModelicaError.

So, when MAP-Lib compiles the MSL external function libraries with the CI, the path for ModelicaUtilities.h used by the C functions is set by the CI, whereas the path to the header file is hard-wired to Modelica/Resources/C-Sources when using the error() function in a user model.

Some tools actually slightly modify 3. to get 1. (@HansOlsson doesn't like "customize" so I won't use that word 😃). For example, Dymola 2025x has a directory Sources, outside the MSL, which contains modified header and source files for ModelicaUtilities. The modifications do not change the interfaces, but add some extra information for the compiler and use tool-specific macros, e.g. DYMOLA_STATIC. Of course John Doe should never use this ModelicaUtilities.h file.

Before #3871 was merged, 3. was found in Modelica/Resources/C-Sources/ModelicaUtilities.h. I think we should put it back there.

So, I'm also in favour of merging #4487 as it is now.

@HansOlsson comments that he would like to remove the IncludeDirectory annotation from Modelica.Utilities.Streams.error. This would remove the asymmetry I noted above about ModelicaUtilities.h 2. I understand this requires tool vendors to put ModelicaUtilities.h in the default include directory, which I guess is where ModelicaUtilities.h 1. is found. To me this is OK, what do other too vendors think?

I am also a bit unsure about the files found under Modelica.Resources.BuildProjects. #4487 restores the Visual Studio 2005 files, which I guess are totally obsolete. It doesn't touch gcc and autotools (why?). Are all those files still meaningful, or are they just dinosaurs that should be removed for good?

Last, but not least, I would recommend to back-port all the changes we eventually make to maint/v4.1.x also onto master, since we want ModelicaUtilities.h to be there for John Doe also in future releases of MSL.

@HansOlsson
Copy link
Contributor

I'm glad to see some consensus building 😃.

Me too.

I am also a bit unsure about the files found under Modelica.Resources.BuildProjects. #4487 restores the Visual Studio 2005 files, which I guess are totally obsolete. It doesn't touch gcc and autotools (why?). Are all those files still meaningful, or are they just dinosaurs that should be removed for good?

For a long time Dymola used Visual Studio 2005 as a base-line for building these libraries. However, due to a breaking interface change we switched to 2015 (it is sort of possible to work around, but the code isn't fully working and it is truly horrible).
For C++ (not sure about C) there are more and more discussions about ABI-breaks at some point.

Note that the gcc-issues are as old or older. The the noreturn-special attributes are for gcc 4.7 (released in 2011), and gcc before 2.8 (released in 1998).

@beutlich
Copy link
Member

beutlich commented Feb 15, 2025

  • in Modelica.Utilities.Streams.error, which just calls ModelicaError.

@casella #4496 addresses this.

It doesn't touch gcc and autotools (why?)

@casella Either it can be seen as bug when ModelicaUtilities.h was removed. Alteratievely, the CPPFLAGS are uses/misused to inject the include directory of ModelicaUtitlities.h.

or are they just dinosaurs that should be removed for good?

They are dinosaurs, but still maintained and used.

Last, but not least, I would recommend to back-port all the changes we eventually make to maint/v4.1.x also onto master, since we want ModelicaUtilities.h to be there for John Doe also in future releases of MSL.

I will create a fresh PR targeting master branch: #4535

beutlich added a commit to beutlich/ModelicaStandardLibrary that referenced this issue Feb 15, 2025
Revert "refs modelica#3867: Remove LICENSE_ModelicaUtilities.txt"

This reverts commit 7fe506f.

Revert "refs modelica#3867: Remove ModelicaUtilities.h"

This reverts commit 1bb1d40.
casella pushed a commit that referenced this issue Feb 25, 2025
* refs #4484: Restore ModelicaUtilities.h

Revert "refs #3867: Remove LICENSE_ModelicaUtilities.txt"

This reverts commit 7fe506f.

Revert "refs #3867: Remove ModelicaUtilities.h"

This reverts commit 1bb1d40.

* Configure MODELICA_UTILITIES_INCLUDE_DIR for CMake

* Modernize and simplify noreturn function attribute/specifier

* Handle C23
* Remove fallback to legacy clang extension
* Remove fallback to legacy gcc2/gcc3 extensions
@casella casella changed the title Provide a bare-bones ModelicaUtilities.h file only providing the ModelicaUtilities function declarations Provide ModelicaUtilities.h file for people writing their own C external function Feb 25, 2025
@maltelenz
Copy link
Contributor

During the MAPLib meeting I volunteered to backport #4535 to the 4.1 branch, but it doesn't apply cleanly, so I withdraw my volunteering.

@beutlich could you prepare a backport PR?

@beutlich
Copy link
Member

beutlich commented Feb 26, 2025 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants