shell_compiler.cpp
1 /*
2  * This file is part of CasADi.
3  *
4  * CasADi -- A symbolic framework for dynamic optimization.
5  * Copyright (C) 2010-2023 Joel Andersson, Joris Gillis, Moritz Diehl,
6  * KU Leuven. All rights reserved.
7  * Copyright (C) 2011-2014 Greg Horn
8  *
9  * CasADi is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 3 of the License, or (at your option) any later version.
13  *
14  * CasADi is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with CasADi; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22  *
23  */
24 
25 
26 #include "shell_compiler.hpp"
27 #include "casadi/core/casadi_misc.hpp"
28 #include "casadi/core/casadi_meta.hpp"
29 #include "casadi/core/casadi_logger.hpp"
30 #include <fstream>
31 
32 // Set default object file suffix
33 #ifndef OBJECT_FILE_SUFFIX
34 #define OBJECT_FILE_SUFFIX CASADI_OBJECT_FILE_SUFFIX
35 #endif // OBJECT_FILE_SUFFIX
36 
37 #include <cstdlib>
38 
39 namespace casadi {
40 
41  extern "C"
42  int CASADI_IMPORTER_SHELL_EXPORT
43  casadi_register_importer_shell(ImporterInternal::Plugin* plugin) {
44  plugin->creator = ShellCompiler::creator;
45  plugin->name = "shell";
46  plugin->doc = ShellCompiler::meta_doc.c_str();
47  plugin->version = CASADI_VERSION;
48  plugin->options = &ShellCompiler::options_;
49  return 0;
50  }
51 
52  extern "C"
53  void CASADI_IMPORTER_SHELL_EXPORT casadi_load_importer_shell() {
55  }
56 
57  ShellCompiler::ShellCompiler(const std::string& name) :
58  ImporterInternal(name) {
59  handle_ = nullptr;
60  }
61 
63  if (handle_) close_shared_library(handle_);
64 
65  if (cleanup_) {
66  if (remove(bin_name_.c_str())) casadi_warning("Failed to remove " + bin_name_);
67  if (remove(obj_name_.c_str())) casadi_warning("Failed to remove " + obj_name_);
68  for (const std::string& s : extra_suffixes_) {
69  std::string name = base_name_+s;
70  remove(name.c_str());
71  }
72  }
73  }
74 
77  {{"compiler",
78  {OT_STRING,
79  "Compiler command"}},
80  {"linker",
81  {OT_STRING,
82  "Linker command"}},
83  {"directory",
84  {OT_STRING,
85  "Directory to put temporary objects in. Must end with a file separator."}},
86  {"compiler_setup",
87  {OT_STRING,
88  "Compiler setup command. Intended to be fixed."
89  " The 'flag' option is the prefered way to set"
90  " custom flags."}},
91  {"linker_setup",
92  {OT_STRING,
93  "Linker setup command. Intended to be fixed."
94  " The 'flag' option is the prefered way to set"
95  " custom flags."}},
96  {"compiler_flags",
98  "Alias for 'compiler_flags'"}},
99  {"flags",
101  "Compile flags for the JIT compiler. Default: None"}},
102  {"linker_flags",
104  "Linker flags for the JIT compiler. Default: None"}},
105  {"cleanup",
106  {OT_BOOL,
107  "Cleanup temporary files when unloading. Default: true"}},
108  {"compiler_output_flag",
109  {OT_STRING,
110  "Compiler flag to denote object output. Default: '-o '"}},
111  {"linker_output_flag",
112  {OT_STRING,
113  "Linker flag to denote shared library output. Default: '-o '"}},
114  {"extra_suffixes",
116  "List of suffixes for extra files that the compiler may generate. Default: None"}},
117  {"name",
118  {OT_STRING,
119  "The file name used to write out compiled objects/libraries. "
120  "The actual file names used depend on 'temp_suffix' and include extensions. "
121  "Default: 'tmp_casadi_compiler_shell'"}},
122  {"temp_suffix",
123  {OT_BOOL,
124  "Use a temporary (seemingly random) filename suffix for file names. "
125  "This is desired for thread-safety. "
126  "This behaviour may defeat caching compiler wrappers. "
127  "Default: true"}},
128  }
129  };
130 
131  void ShellCompiler::init(const Dict& opts) {
132  // Base class
134 
135  // Default options
136 
137  cleanup_ = true;
138  bool temp_suffix = true;
139  std::string bare_name = "tmp_casadi_compiler_shell";
140  std::string directory = "";
141 
142  std::vector<std::string> compiler_flags;
143  std::vector<std::string> linker_flags;
144  std::string suffix = OBJECT_FILE_SUFFIX;
145 
146 #ifdef _WIN32
147  std::string compiler = "cl.exe";
148  std::string linker = "link.exe";
149  std::string compiler_setup = "/c";
150  std::string linker_setup = "/DLL";
151  std::string compiler_output_flag = "/Fo";
152  std::string linker_output_flag = "/out:";
153  extra_suffixes_ = {".exp", ".lib"};
154 #else
155  std::string compiler = "gcc";
156  std::string linker = "gcc";
157  std::string compiler_setup = "-fPIC -c";
158  std::string linker_setup = "-shared";
159  std::string compiler_output_flag = "-o ";
160  std::string linker_output_flag = "-o ";
161 #endif
162 
163  // Read options
164  for (auto&& op : opts) {
165  if (op.first=="compiler") {
166  compiler = op.second.to_string();
167  } else if (op.first=="linker") {
168  linker = op.second.to_string();
169  } else if (op.first=="directory") {
170  directory = op.second.to_string();
171  } else if (op.first=="compiler_setup") {
172  compiler_setup = op.second.to_string();
173  } else if (op.first=="cleanup") {
174  cleanup_ = op.second;
175  } else if (op.first=="linker_setup") {
176  linker_setup = op.second.to_string();
177  } else if (op.first=="compiler_flags" || op.first=="flags") {
178  compiler_flags = op.second;
179  } else if (op.first=="linker_flags") {
180  linker_flags = op.second;
181  } else if (op.first=="compiler_output_flag") {
182  compiler_output_flag = op.second.to_string();
183  } else if (op.first=="linker_output_flag") {
184  linker_output_flag = op.second.to_string();
185  } else if (op.first=="extra_suffixes") {
186  extra_suffixes_ = op.second.to_string_vector();
187  } else if (op.first=="name") {
188  bare_name = op.second.to_string();
189  } else if (op.first=="temp_suffix") {
190  temp_suffix = op.second;
191  }
192  }
193 
194  // Name of temporary file
195  if (temp_suffix) {
196  obj_name_ = temporary_file(directory + bare_name, suffix);
197  } else {
198  obj_name_ = directory + bare_name + suffix;
199  }
200  base_name_ = std::string(obj_name_.begin(), obj_name_.begin()+obj_name_.size()-suffix.size());
201  bin_name_ = base_name_+SHARED_LIBRARY_SUFFIX;
202 
203 #ifndef _WIN32
204  // Have relative paths start with ./
205  if (obj_name_.at(0)!='/') {
206  obj_name_ = "./" + obj_name_;
207  }
208 
209  if (bin_name_.at(0)!='/') {
210  bin_name_ = "./" + bin_name_;
211  }
212 #endif // _WIN32
213 
214  // Construct the compiler command
215  std::stringstream cccmd;
216  cccmd << compiler;
217  for (auto i=compiler_flags.begin(); i!=compiler_flags.end(); ++i) {
218  cccmd << " " << *i;
219  }
220  cccmd << " " << compiler_setup;
221 
222  // C/C++ source file
223  cccmd << " " << name_;
224 
225  // Temporary object file
226  cccmd << " " + compiler_output_flag << obj_name_;
227 
228  // Compile into an object
229  if (verbose_) casadi_message("calling \"" + cccmd.str() + "\"");
230  if (system(cccmd.str().c_str())) {
231  casadi_error("Compilation failed. Tried \"" + cccmd.str() + "\"");
232  }
233 
234  // Link step
235  std::stringstream ldcmd;
236  ldcmd << linker;
237 
238  // Temporary file
239  ldcmd << " " << obj_name_ << " " + linker_output_flag + bin_name_;
240 
241  // Add flags
242  for (auto i=linker_flags.begin(); i!=linker_flags.end(); ++i) {
243  ldcmd << " " << *i;
244  }
245  ldcmd << " " << linker_setup;
246 
247  // Compile into a shared library
248  if (verbose_) casadi_message("calling \"" + ldcmd.str() + "\"");
249  if (system(ldcmd.str().c_str())) {
250  casadi_error("Linking failed. Tried \"" + ldcmd.str() + "\"");
251  }
252 
253  std::vector<std::string> search_paths = get_search_paths();
254  handle_ = open_shared_library(bin_name_, search_paths, "ShellCompiler::init");
255 
256  }
257 
258  std::string ShellCompiler::library() const {
259  return bin_name_;
260  }
261 
262  signal_t ShellCompiler::get_function(const std::string& symname) {
263 #ifdef _WIN32
264  return (signal_t)GetProcAddress(handle_, TEXT(symname.c_str()));
265 #else // _WIN32
266  signal_t fcnPtr = reinterpret_cast<signal_t>(dlsym(handle_, symname.c_str()));
267  if (dlerror()) {
268  fcnPtr=nullptr;
269  dlerror(); // Reset error flags
270  }
271  return fcnPtr;
272 #endif // _WIN32
273  }
274 
275 } // namespace casadi
Importer internal class.
bool verbose_
Verbose – for debugging purposes.
virtual void init(const Dict &opts)
Initialize.
static const Options options_
Options.
std::string name_
C filename.
static void registerPlugin(const Plugin &plugin, bool needs_lock=true)
Register an integrator in the factory.
std::string bin_name_
Temporary file.
std::string obj_name_
Temporary file.
static const Options options_
Options.
static ImporterInternal * creator(const std::string &name)
Create a new JIT function.
ShellCompiler(const std::string &name)
Constructor.
std::vector< std::string > extra_suffixes_
Extra files.
static const std::string meta_doc
A documentation string.
~ShellCompiler() override
Destructor.
bool cleanup_
Cleanup temporary files when unloading.
signal_t get_function(const std::string &symname) override
Get a function pointer for numerical evaluation.
void init(const Dict &opts) override
Initialize.
std::string library() const override
Get library name.
The casadi namespace.
Definition: archiver.cpp:28
std::string temporary_file(const std::string &prefix, const std::string &suffix)
int CASADI_IMPORTER_SHELL_EXPORT casadi_register_importer_shell(ImporterInternal::Plugin *plugin)
std::vector< std::string > get_search_paths()
Definition: casadi_os.cpp:49
@ OT_STRINGVECTOR
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.
void(* signal_t)(void)
Function pointer types for the C API.
bool remove(const std::string &path)
Definition: ghc.cpp:47
void CASADI_IMPORTER_SHELL_EXPORT casadi_load_importer_shell()
Options metadata for a class.
Definition: options.hpp:40