Arcane  v3.16.7.0
Documentation développeur
Chargement...
Recherche...
Aucune correspondance
ArcaneCoreClr.cc
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2025 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
4// See the top-level COPYRIGHT file for details.
5// SPDX-License-Identifier: Apache-2.0
6//-----------------------------------------------------------------------------
7
8// Licensed to the .NET Foundation under one or more agreements.
9// The .NET Foundation licenses this file to you under the MIT license.
10// See the LICENSE file in the project root for more information.
11
13
14#include "arcane/utils/PlatformUtils.h"
15#include "arcane/utils/String.h"
16#include "arcane/utils/CommandLineArguments.h"
17#include "arcane/utils/FatalErrorException.h"
18#include "arcane/utils/Exception.h"
19#include "arcane/utils/CheckedConvert.h"
20#include "arcane/core/Directory.h"
21
22// Standard headers
23#include <stdio.h>
24#include <stdint.h>
25#include <stdlib.h>
26#include <string.h>
27#include <assert.h>
28#include <iostream>
29
30// NOTE: à partir de '.Net 6', ces fichiers sont inclus dans le SDK
31// au même endroit que 'libnethost.so'
32
33// Provided by the AppHost NuGet package and installed as an SDK pack
34#include <nethost.h>
35#include <coreclr_delegates.h>
36#include <hostfxr.h>
37
38#include <iostream>
39
40using string_t = std::basic_string<char_t>;
41
42/*---------------------------------------------------------------------------*/
43/*---------------------------------------------------------------------------*/
44
45#ifdef _WINDOWS
46
47#include <windows.h>
48
49#define STR(s) L##s
50#define CH(c) L##c
51
52namespace
53{
54 std::wstring _toString(const Arcane::String& s)
55 {
57 }
58 char_t* _duplicate(const char_t* x)
59 {
60 return ::_wcsdup(x);
61 }
62 Arcane::String _toArcaneString(const char_t* x)
63 {
64 std::wstring_view wstr_view(x);
66 }
67} // namespace
68
69#else
70
71// UNIX
72
73#include <dlfcn.h>
74#include <limits.h>
75
76#define STR(s) s
77#define CH(c) c
78#define MAX_PATH PATH_MAX
79typedef char char_t;
80namespace
81{
82 string_t _toString(const Arcane::String& s)
83 {
84 return std::string(s.toStdStringView());
85 }
86 char_t* _duplicate(const char_t* x)
87 {
88 return ::strdup(x);
89 }
90 Arcane::String _toArcaneString(const char_t* x)
91 {
93 }
94} // namespace
95
96#endif
97
98/*---------------------------------------------------------------------------*/
99/*---------------------------------------------------------------------------*/
100
101using namespace Arcane;
102
103namespace
104{
105// Indique si on affiche les informations de debug
106int dotnet_verbose = 0;
107
108#ifdef _WINDOWS
109using LibHandle = HMODULE;
110#else
111using LibHandle = void*;
112#endif
113
114}
115
116namespace Arcane
117{
119{
120 LibHandle m_lib_handle = (LibHandle)0;
121 bool m_has_valid_lib_handle = false;
122
123 hostfxr_initialize_for_runtime_config_fn init_fptr = nullptr;
124 hostfxr_initialize_for_dotnet_command_line_fn init_command_line_fptr = nullptr;
125 hostfxr_get_runtime_delegate_fn get_delegate_fptr = nullptr;
126 hostfxr_run_app_fn run_app_fptr = nullptr;
127 hostfxr_close_fn close_fptr = nullptr;
128
129 void cleanup();
130};
131
132}
133
134#define PRINT_FORMAT(level,str,...) \
135 if (dotnet_verbose>=level)\
136 std::cout << String::format("[coreclr] " str "\n",__VA_ARGS__);
137
138namespace
139{
140#if defined(ARCANE_DOTNET_ROOT)
141const char* arcane_dotnet_root = ARCANE_DOTNET_ROOT;
142#else
143const char* arcane_dotnet_root = nullptr;
144#endif
146// Utile pour conserver la valeur de la variable d'environnement
147// DOTNET_ROOT
148std::string arcane_dotnet_root_env_variable;
149
150// Globals to hold hostfxr exports
151
152// Forward declarations
153bool load_hostfxr(const string_t& assembly_name);
154
155// Load and initialize .NET Core and get desired function pointer for scenario
156// (Note: pas utilisé pour l'instant).
157load_assembly_and_get_function_pointer_fn
158getDotnetLoadAssembly(const String& assembly)
159{
160 string_t assembly1 = _toString(assembly);
161 // Load .NET Core
162 void* load_assembly_and_get_function_pointer = nullptr;
163 hostfxr_handle cxt = nullptr;
164
165 hostfxr_initialize_parameters params;
166 params.size = sizeof(params);
167 // TODO: il s'agit du chemin de l'exécutable, pas de l'assembly.
168 params.host_path = assembly1.c_str(); //get_path_to_the_host_exe(); // Path to the current executable
169
170 int rc = lib_info.init_fptr(assembly1.c_str(), &params, &cxt);
171 if (rc != 0 || cxt == nullptr) {
172 auto flags = std::cerr.flags();
173 std::cerr << "Init failed: " << std::hex << std::showbase << rc << std::endl;
174 std::cerr.setf(flags);
175 lib_info.close_fptr(cxt);
176 return nullptr;
177 }
178
179 // Get the load assembly function pointer
180 rc = lib_info.get_delegate_fptr(cxt, hdt_load_assembly_and_get_function_pointer,
181 &load_assembly_and_get_function_pointer);
182 if (rc != 0 || load_assembly_and_get_function_pointer == nullptr)
183 std::cerr << "Get delegate failed: " << std::hex << std::showbase << rc << std::endl;
184
185 lib_info.close_fptr(cxt);
186 return (load_assembly_and_get_function_pointer_fn)load_assembly_and_get_function_pointer;
187}
188
189/*---------------------------------------------------------------------------*/
190/*---------------------------------------------------------------------------*/
191// Pour tester le lancement direct. En cours d'étude.
192
193int
194_execDirect(const CommandLineArguments& cmd_args,
195 const String& orig_assembly_name)
196{
197 using const_char_t = const char_t*;
198
199 int argc = *(cmd_args.commandLineArgc());
200 //const char** old_argv = (const char**)*(cmd_args.commandLineArgv());
201
202 Arcane::String root_path = Arcane::platform::getFileDirName(orig_assembly_name);
203 string_t root_path1 = _toString(root_path);
204 string_t orig_assembly_name1 = _toString(orig_assembly_name);
205 std::cerr << "ENTERING _execDirect root_path=" << root_path << "\n";
206
207 string_t dotnet_root = _toString(String(arcane_dotnet_root));
208 hostfxr_initialize_parameters params;
209 params.size = sizeof(params);
210 params.host_path = root_path1.c_str();
211#ifdef ARCANE_OS_WIN32
212 // Sous Windows, '.Net' est installé dans un chemin standard
213 // et il ne faut pas spécifier le chemin (cela provoque une erreur
214 // d'argument invalide (a vérifier si c'est parce que 'arcane_dotnet_root'
215 // n'est pas valide ou s'il ne faut rien spécifier).
216 params.dotnet_root = nullptr;
217#else
218 params.dotnet_root = dotnet_root.c_str();
219#endif
220 const_char_t* argv = new const_char_t[1];
221 char_t* argv0_str = _duplicate((const char_t*)(orig_assembly_name1.c_str()));
222 argv[0] = argv0_str;
223 std::cerr << "_execDirect argv[0] =" << orig_assembly_name << "\n";
224 argc = 1;
225
226 hostfxr_handle host_context_handle;
227 int rc = lib_info.init_command_line_fptr(argc, (const char_t**)argv, &params, &host_context_handle);
228 std::cerr << "_execDirect init_command_line R = " << rc << "\n";
229 if (rc!=0)
230 ARCANE_FATAL("Can not initialize runtime RC={0}",rc);
231
232#if TEST
233 size_t buffer_used = 0;
234 if (hostfxr_get_runtime_property(host_context_handle, "TEST_PROPERTY", nullptr, 0, &buffer_used) == HostApiMissingProperty) {
235 hostfxr_set_runtime_property(host_context_handle, "TEST_PROPERTY", "TRUE");
236 }
237#endif
238
239 std::cerr << "Launching '.Net'\n";
240 int r = lib_info.run_app_fptr(host_context_handle);
241 std::cerr << "End '.Net': R=" << r << "\n";
242
243 ::free(argv0_str);
244 lib_info.close_fptr(host_context_handle);
245 return r;
246}
247} // namespace
248
249/*---------------------------------------------------------------------------*/
250/*---------------------------------------------------------------------------*/
254
255int
256_arcaneCoreClrMainInternal(const Arcane::CommandLineArguments& cmd_args,
257 const Arcane::String& orig_assembly_name)
258{
259 String verbose_str = Arcane::platform::getEnvironmentVariable("ARCANE_DEBUG_DOTNET");
260 if (!verbose_str.null())
261 dotnet_verbose = 1;
262
263 // Si l'utilisateur a spécifié cette variable, alors on ne surcharge pas
264 // les appels avec la version configurée lors de la compilation.
265 // Le runtime 'coreclr' se charge d'utiliser cette variable d'environnement.
266
267 String dotnet_root_env = platform::getEnvironmentVariable("DOTNET_ROOT");
268 if (!dotnet_root_env.null()){
269 arcane_dotnet_root_env_variable = dotnet_root_env.toStdStringView();
270 arcane_dotnet_root = arcane_dotnet_root_env_variable.c_str();
271 }
272
273 // TODO: trouver un moyen d'utiliser 'cmd_args'
274 PRINT_FORMAT(1,"ARCANE_DOTNET_CORECLR_MAIN assembly_name={0}",orig_assembly_name);
275
276 if (orig_assembly_name.empty())
277 ARCANE_FATAL("No assembly name");
278
279 string_t orig_assembly_name1 = _toString(orig_assembly_name);
280
281 String root_path = Arcane::platform::getFileDirName(orig_assembly_name);
282
283 PRINT_FORMAT(1,"ENTERING CORECLR_MAIN root_path={0}",root_path);
284
285 //
286 // STEP 1: Load HostFxr and get exported hosting functions
287 //
288 if (!load_hostfxr(orig_assembly_name1))
289 ARCANE_FATAL("Failure: load_hostfxr()");
290
291 const bool do_direct_exec = true;
292 if (do_direct_exec)
293 return _execDirect(cmd_args, orig_assembly_name);
294
295 // NOTE: Cette partie n'est pas utilisée pour l'instant.
296
297 //
298 // STEP 2: Initialize and start the .NET Core runtime
299 //
300 Arcane::String config_path = Arcane::Directory(root_path).file("Arcane.Main.runtimeconfig.json");
301 load_assembly_and_get_function_pointer_fn load_assembly_and_get_function_pointer = nullptr;
302 load_assembly_and_get_function_pointer = getDotnetLoadAssembly(config_path);
303 assert(load_assembly_and_get_function_pointer != nullptr && "Failure: get_dotnet_load_assembly()");
304
305 //
306 // STEP 3: Load managed assembly and get function pointer to a managed method
307 //
308 //const string_t dotnetlib_path = root_path + STR("Arcane.Main.dll");
309 const char_t* dotnet_type = STR("ArcaneMainExec, Arcane.Main");
310 const char_t* dotnet_type_method = STR("CoreClrComponentEntryPoint");
311 // <SnippetLoadAndGet>
312 // Function pointer to managed delegate
313 component_entry_point_fn dll_entry_point_func = nullptr;
314 // ATTENTION: si on passe 'nullptr' comme 'delegate_type_name', alors 'dotnet_type_method'
315 // doit être du type 'int (InpPtr args,int sizeBytes)'.
316 int rc = load_assembly_and_get_function_pointer(orig_assembly_name1.c_str(),
317 dotnet_type,
318 dotnet_type_method,
319 nullptr /*delegate_type_name*/,
320 nullptr,
321 (void**)&dll_entry_point_func);
322 // </SnippetLoadAndGet>
323 if (rc != 0)
324 ARCANE_FATAL("load_assembly_and_get_function_pointer: rc={0}", rc);
325 if (!dll_entry_point_func)
326 ARCANE_FATAL("Failure: load_assembly_and_get_function_pointer()");
327
328 //
329 // STEP 4: Run managed code
330 //
331 struct lib_args
332 {
333 const char_t* message;
334 int number;
335 };
336 lib_args args{ STR("from host!"), 0 };
337
338 return dll_entry_point_func(&args, sizeof(args));
339}
340
341/*---------------------------------------------------------------------------*/
342/*---------------------------------------------------------------------------*/
343
344// Point d'entrée appelé par ArcaneMain
345extern "C" ARCANE_EXPORT int
346arcane_dotnet_coreclr_main(const Arcane::CommandLineArguments& cmd_args,
347 const Arcane::String& orig_assembly_name)
348{
349 int ret = 0;
350 try{
351 ret = _arcaneCoreClrMainInternal(cmd_args,orig_assembly_name);
352 }
353 catch(const Exception& ex){
354 ret = arcanePrintArcaneException(ex,nullptr);
355 }
356 catch(const std::exception& ex){
357 ret = arcanePrintStdException(ex,nullptr);
358 }
359 catch(...){
360 ret = arcanePrintAnyException(nullptr);
361 }
362 return ret;
363}
364
365/*---------------------------------------------------------------------------*/
366/*---------------------------------------------------------------------------*/
367
368/********************************************************************************************
369 * Function used to load and activate .NET Core
370 ********************************************************************************************/
371
372namespace
373{
374
375// Forward declarations
376LibHandle load_library(const char_t*);
377void* get_export(LibHandle, const char*);
378
379#ifdef _WINDOWS
380LibHandle load_library(const char_t* path)
381{
382 HMODULE h = ::LoadLibraryW(path);
383 if (!h)
384 ARCANE_FATAL("Can not load library '{0}'",_toArcaneString(path));
385 return h;
386}
387void free_library(LibHandle h)
388{
389 FreeLibrary(h);
390}
391void* get_export(LibHandle h, const char* name)
392{
393 void* f = ::GetProcAddress(h, name);
394 if (!f)
395 ARCANE_FATAL("Can not get library symbol '{0}'",name);
396 return f;
397}
398#else
399LibHandle load_library(const char_t* path)
400{
401 void* h = dlopen(path, RTLD_LAZY | RTLD_LOCAL);
402 if (!h)
403 ARCANE_FATAL("Can not load library '{0}' error='{1}'",path,dlerror());
404 return h;
405}
406void free_library(LibHandle h)
407{
408 ::dlclose(h);
409}
410void* get_export(LibHandle h, const char* name)
411{
412 void* f = dlsym(h, name);
413 PRINT_FORMAT(1,"get_export name={0} f={1}",name,f);
414 if (!f)
415 PRINT_FORMAT(0,"Can not get library symbol '{0}' error='{1}'",name,dlerror());
416 return f;
417}
418#endif
419
420// <SnippetLoadHostFxr>
421// Using the nethost library, discover the location of hostfxr and get exports
422bool
423load_hostfxr(const string_t& assembly_name)
424{
425 // Si 'coreclr' n'est pas installé dans un chemin standard, il est possible
426 // qu'il faille le spécifier. La variable d'environnement DOTNET_ROOT permet
427 // de le faire. Si elle n'est pas positionnée, utilise le chemin trouvée
428 // lors de la configuration.
429 string_t dotnet_root = _toString(String(arcane_dotnet_root));
430 get_hostfxr_parameters hostfxr_parameters;
431 hostfxr_parameters.size = sizeof(get_hostfxr_parameters);
432 hostfxr_parameters.assembly_path = assembly_name.c_str();
433 hostfxr_parameters.dotnet_root = dotnet_root.c_str();
434
435 PRINT_FORMAT(1,"Entering load_hostfxr() dotnet_root={0}",arcane_dotnet_root);
436 // Pre-allocate a large buffer for the path to hostfxr
437 const int BUF_LEN = 12000;
438 char_t buffer[BUF_LEN];
439 size_t buffer_size = sizeof(buffer) / sizeof(char_t);
440
441 // List of return values for 'get_hostfxr_path' are here:
442 // https://github.com/dotnet/runtime/blob/main/docs/design/features/host-error-codes.md
443 // Real good value is '0'.
444 // Positive values are for warnings
445 // Negative valeurs are for errors
446 int rc = get_hostfxr_path(buffer, &buffer_size, &hostfxr_parameters);
447 PRINT_FORMAT(1,"Return value of 'get_hostfxr_path' = '{0}'",rc);
448 if (rc != 0)
449 PRINT_FORMAT(0,"Error or warning calling 'get_hostfxr_path' = '{0}'",rc);
450 if (rc < 0)
451 return false;
452
453 // Load hostfxr and get desired exports
454 LibHandle lib = load_library(buffer);
455 PRINT_FORMAT(1,"LIB_PTR={0} path={1}",lib,_toArcaneString(buffer));
456 lib_info.m_lib_handle = lib;
457 lib_info.m_has_valid_lib_handle = true;
458 lib_info.init_fptr = (hostfxr_initialize_for_runtime_config_fn)get_export(lib, "hostfxr_initialize_for_runtime_config");
459 lib_info.get_delegate_fptr = (hostfxr_get_runtime_delegate_fn)get_export(lib, "hostfxr_get_runtime_delegate");
460 lib_info.close_fptr = (hostfxr_close_fn)get_export(lib, "hostfxr_close");
461 lib_info.init_command_line_fptr = (hostfxr_initialize_for_dotnet_command_line_fn)get_export(lib, "hostfxr_initialize_for_dotnet_command_line");
462 lib_info.run_app_fptr = (hostfxr_run_app_fn)get_export(lib, "hostfxr_run_app");
463 return (lib_info.init_fptr && lib_info.get_delegate_fptr && lib_info.close_fptr && lib_info.init_command_line_fptr && lib_info.run_app_fptr);
464}
465
466} // namespace
467
468namespace Arcane
469{
470
471void CoreClrLibInfo::
472cleanup()
473{
474 if (m_has_valid_lib_handle){
475 free_library(m_lib_handle);
476 m_lib_handle = (LibHandle)0;
477 }
478 m_has_valid_lib_handle = false;
479}
480
481}
482
483/*---------------------------------------------------------------------------*/
484/*---------------------------------------------------------------------------*/
#define ARCANE_FATAL(...)
Macro envoyant une exception FatalErrorException.
Fonctions utilitaires sur les chaînes de caractères.
ARCCORE_BASE_EXPORT std::wstring convertToStdWString(const String &str)
Retourne la conversion de str en std::wstring.
Definition String.cc:1288
ARCCORE_BASE_EXPORT String convertToArcaneString(const std::wstring_view &wstr)
Convertie wstr en une String.
Definition String.cc:1305
Arguments de la ligne de commande.
Classe gérant un répertoire.
Definition Directory.h:35
String file(const String &file_name) const override
Retourne le chemin complet du fichier file_name dans le répertoire.
Definition Directory.cc:120
Classe de base d'une exception.
Vue sur une chaîne de caractères UTF-8.
Definition StringView.h:47
Chaîne de caractères unicode.
bool null() const
Retourne true si la chaîne est nulle.
Definition String.cc:305
bool empty() const
Vrai si la chaîne est vide (nulle ou "")
Definition String.cc:316
std::string_view toStdStringView() const
Retourne une vue de la STL sur la chaîne actuelle.
Definition String.cc:349
ARCCORE_BASE_EXPORT String getFileDirName(const String &file_name)
Retourne le nom du répertoire d'un fichier.
ARCCORE_BASE_EXPORT String getEnvironmentVariable(const String &name)
Variable d'environnement du nom name.
-*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-