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