Panda3D
mayapath.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file mayapath.cxx
10  * @author drose
11  * @date 2008-04-07
12  */
13 
14 // This program works as a stub to launch maya2egg, egg2maya, and similar
15 // programs that invoke OpenMaya and require certain environment variables to
16 // be set first.
17 
18 // It used to duplicate code in mayaWrapper.cxx, but now the functionality for
19 // these two separate programs are unified here.
20 
21 // If MAYAVERSION is defined at the time this is compiled, then that
22 // particular version of Maya is insisted upon, and the desired Maya location
23 // is found in the Registry; otherwise, we require that $MAYA_LOCATION be set
24 // at runtime and points to the desired Maya installation.
25 
26 /*
27  * If MAYAVERSION is defined and $MAYA_LOCATION is also set, then we check
28  * that definition of $MAYA_LOCATION is reasonable, which we define as
29  * pointing to the same version of OpenMaya.dll. If so, then we use the
30  * runtime $MAYA_LOCATION, allowing the user to (slightly) override the
31  * runtime Maya directory. If $MAYA_LOCATION is set but points to a different
32  * version of OpenMaya.dll, we ignore it altogether and replace it with our
33  * registry data, which allows the user to have MAYA_LOCATION pointing to a
34  * different version of Maya without interfering with this program.
35  */
36 
37 #include "dtoolbase.h"
38 #include "filename.h"
39 #include "globPattern.h"
40 #include "dSearchPath.h"
41 #include "executionEnvironment.h"
42 #include "hashVal.h"
43 #include <stdlib.h>
44 
45 #if defined(_WIN32)
46 #ifndef WIN32_LEAN_AND_MEAN
47 #define WIN32_LEAN_AND_MEAN
48 #endif
49 #include <windows.h>
50 #else
51 #include <sys/stat.h>
52 #endif
53 
54 using std::cerr;
55 using std::endl;
56 using std::string;
57 
58 #define QUOTESTR(x) #x
59 #define TOSTRING(x) QUOTESTR(x)
60 
61 #if defined(_WIN32)
62 // Note: Filename::dso_filename changes .so to .dll automatically.
63 static const Filename openmaya_filename = "bin/OpenMaya.so";
64 #elif defined(IS_OSX)
65 static const Filename openmaya_filename = "MacOS/libOpenMaya.dylib";
66 #else
67 static const Filename openmaya_filename = "lib/libOpenMaya.so";
68 #endif // _WIN32
69 
70 // Searches for python26.zip or whatever version it is.
71 static Filename
72 find_pyzip(const Filename &maya_location) {
73  // This is where python26.zip appears on Windows. Should it be in other
74  // locations on other platforms?
75  Filename dirname(maya_location, "bin");
76 
77  vector_string results;
78  GlobPattern glob("python*.zip");
79  if (glob.match_files(results, dirname) != 0) {
80  return Filename(dirname, results[0]);
81  }
82 
83  return Filename();
84 }
85 
86 struct MayaVerInfo {
87  const char *ver, *key;
88 };
89 
90 struct MayaVerInfo maya_versions[] = {
91  { "MAYA6", "6.0" },
92  { "MAYA65", "6.5" },
93  { "MAYA7", "7.0" },
94  { "MAYA8", "8.0" },
95  { "MAYA85", "8.5" },
96  { "MAYA2008", "2008"},
97  { "MAYA2009", "2009"},
98  { "MAYA2010", "2010"},
99  { "MAYA2011", "2011"},
100  { "MAYA2012", "2012"},
101  { "MAYA2013", "2013"},
102  { "MAYA20135", "2013.5"},
103  { "MAYA2014", "2014"},
104  { "MAYA2015", "2015"},
105  { "MAYA2016", "2016"},
106  { "MAYA20165", "2016.5"},
107  { "MAYA2017", "2017"},
108  { "MAYA2018", "2018"},
109  { 0, 0 },
110 };
111 
112 static const char *
113 get_version_number(const char *ver) {
114  for (int i = 0; maya_versions[i].ver != 0; ++i) {
115  if (strcmp(maya_versions[i].ver, ver) == 0) {
116  return maya_versions[i].key;
117  }
118  }
119  return 0;
120 }
121 
122 #if defined(_WIN32)
123 static void
124 get_maya_location(const char *ver, string &loc) {
125  char fullkey[1024];
126  const char *developer;
127  LONG res;
128 
129  for (int dev=0; dev<3; dev++) {
130  switch (dev) {
131  case 0: developer="Alias|Wavefront"; break;
132  case 1: developer="Alias"; break;
133  case 2: developer="Autodesk"; break;
134  }
135  sprintf(fullkey, "SOFTWARE\\%s\\Maya\\%s\\Setup\\InstallPath", developer, ver);
136  for (int hive=0; hive<2; hive++) {
137  HKEY hkey;
138  res = RegOpenKeyEx(HKEY_LOCAL_MACHINE, fullkey, 0, KEY_READ | (hive ? 256:0), &hkey);
139  if (res == ERROR_SUCCESS) {
140  DWORD dtype;
141  DWORD size = 4096;
142  char result[4096 + 1];
143  res = RegQueryValueEx(hkey, "MAYA_INSTALL_LOCATION", nullptr, &dtype, (LPBYTE)result, &size);
144  if ((res == ERROR_SUCCESS)&&(dtype == REG_SZ)) {
145  result[size] = 0;
146  loc = result;
147  }
148  RegCloseKey(hkey);
149  }
150  }
151  }
152 }
153 
154 #elif defined(__APPLE__)
155 static void
156 get_maya_location(const char *ver, string &loc) {
157  char mpath[64];
158  sprintf(mpath, "/Applications/Autodesk/maya%s/Maya.app/Contents", ver);
159  struct stat st;
160  if(stat(mpath, &st) == 0) {
161  loc = mpath;
162  }
163 }
164 
165 #else // _WIN32
166 static void
167 get_maya_location(const char *ver, string &loc) {
168  char mpath[64];
169 #if __WORDSIZE == 64
170  sprintf(mpath, "/usr/autodesk/maya%s-x64", ver);
171 #else
172  sprintf(mpath, "/usr/autodesk/maya%s", ver);
173 #endif
174  struct stat st;
175  if(stat(mpath, &st) == 0) {
176  loc = mpath;
177  } else {
178 #if __WORDSIZE == 64
179  sprintf(mpath, "/usr/aw/maya%s-x64", ver);
180 #else
181  sprintf(mpath, "/usr/aw/maya%s", ver);
182 #endif
183  if(stat(mpath, &st) == 0) {
184  loc = mpath;
185  }
186  }
187 }
188 
189 #endif // _WIN32
190 
191 
192 int
193 main(int argc, char *argv[]) {
194  // First, get the command line and append _bin, so we will actually run
195  // maya2egg_bin.exe, egg2maya_bin.exe, etc.
196  Filename command = Filename::from_os_specific(argv[0]);
197  if (!command.is_fully_qualified()) {
198  DSearchPath path;
200 #ifdef _WIN32
201  command.set_extension("exe");
202 #endif
203  command.resolve_filename(path);
204  }
205 
206 #ifdef _WIN32
207  if (command.get_extension() == "exe") {
208  command.set_extension("");
209  }
210 #endif
211 
212  command = command.get_fullpath() + string("_bin");
213 #ifdef _WIN32
214  command.set_extension("exe");
215 #endif
216  string os_command = command.to_os_specific();
217 
218  // First start with $PANDA_MAYA_LOCATION. If it is set, it overrides
219  // everything else.
220  Filename maya_location = Filename::expand_from("$PANDA_MAYA_LOCATION");
221  if (!maya_location.empty()) {
222  // Reset maya_location to its full long name, because Maya requires this.
223  maya_location.make_canonical();
224  maya_location = Filename::from_os_specific(maya_location.to_os_long_name());
225 
226  } else {
227  // $PANDA_MAYA_LOCATION wasn't set, so check the normal locations. First,
228  // we get the standard location, as a point of reference.
229  Filename standard_maya_location;
230 #ifdef MAYAVERSION
231  const char *key = get_version_number(TOSTRING(MAYAVERSION));
232  if (key == nullptr) {
233  cerr << "Unknown Maya version: " << TOSTRING(MAYAVERSION) << "\n";
234  } else {
235  string loc;
236  get_maya_location(key, loc);
237  if (loc.empty()) {
238  cerr << "Cannot locate " << TOSTRING(MAYAVERSION) << ": it does not appear to be installed.\n";
239  } else {
240  standard_maya_location = Filename::from_os_specific(loc);
241  }
242  }
243  if (!standard_maya_location.empty()) {
244  // Reset standard_maya_location to its full long name, so we can compare
245  // reliably to the given version.
246  standard_maya_location.make_canonical();
247  standard_maya_location = Filename::from_os_specific(standard_maya_location.to_os_long_name());
248  }
249 #endif // MAYAVERSION
250 
251  // Now check if $MAYA_LOCATION is set. If it is, and it's consistent with
252  // the standard location, we respect it.
253  maya_location = Filename::expand_from("$MAYA_LOCATION");
254  if (!maya_location.empty()) {
255  // Reset maya_location to its full long name, so we can compare it
256  // reliably to the standard location; and also because Maya requires
257  // this.
258  maya_location.make_canonical();
259  maya_location = Filename::from_os_specific(maya_location.to_os_long_name());
260  }
261 
262  if (maya_location.empty()) {
263  // If it is not set, we use the standard version instead.
264  maya_location = standard_maya_location;
265 
266  } else if (maya_location != standard_maya_location) {
267  // If it *is* set, we verify that OpenMaya.dll matches the standard
268  // version.
269  Filename openmaya_given = Filename::dso_filename(Filename(maya_location, openmaya_filename));
270  Filename openmaya_standard = Filename::dso_filename(Filename(standard_maya_location, openmaya_filename));
271 
272  if (openmaya_given != openmaya_standard) {
273 #ifdef HAVE_OPENSSL
274  // If we have OpenSSL, we can use it to check the md5 hashes of the
275  // DLL.
276  HashVal hash_given, hash_standard;
277  if (!hash_standard.hash_file(openmaya_standard)) {
278  // Couldn't read the standard file, so use the given one.
279 
280  } else {
281  if (!hash_given.hash_file(openmaya_given)) {
282  // Couldn't even read the given file; use the standard one
283  // instead.
284  maya_location = standard_maya_location;
285 
286  } else {
287  if (hash_standard != hash_given) {
288  // No match; it must be the wrong version.
289  cerr << "$MAYA_LOCATION points to wrong version; using standard location instead.\n";
290  maya_location = standard_maya_location;
291  } else {
292  // The hash matches; keep the given MAYA_LOCATION setting.
293  }
294  }
295  }
296 #else // HAVE_OPENSSL
297  // Without OpenSSL, just check the DLL filesize only.
298  off_t size_given, size_standard;
299  size_standard = openmaya_standard.get_file_size();
300  if (size_standard == 0) {
301  // Couldn't read the standard file, so use the given one.
302 
303  } else {
304  size_given = openmaya_given.get_file_size();
305  if (size_given == 0) {
306  // Couldn't even read the given file; use the standard one
307  // instead.
308  maya_location = standard_maya_location;
309 
310  } else {
311  if (size_standard != size_given) {
312  // No match; it must be the wrong version.
313  cerr << "$MAYA_LOCATION points to wrong version; using standard location instead.\n";
314  maya_location = standard_maya_location;
315 
316  } else {
317  // The size matches; keep the given MAYA_LOCATION setting.
318  }
319  }
320  }
321 
322 #endif // HAVE_OPENSSL
323  }
324  }
325  }
326 
327  if (maya_location.empty()) {
328  cerr << "$MAYA_LOCATION is not set!\n";
329  exit(1);
330  }
331 
332  cerr << "MAYA_LOCATION: " << maya_location.to_os_specific() << endl;
333  if (!maya_location.is_directory()) {
334  cerr << "The directory referred to by $MAYA_LOCATION does not exist!\n";
335  exit(1);
336  }
337 
338  // Look for OpenMaya.dll as a sanity check.
339  Filename openmaya = Filename::dso_filename(Filename(maya_location, openmaya_filename));
340  if (!openmaya.is_regular_file()) {
341  cerr << "Could not find $MAYA_LOCATION/" << Filename::dso_filename(openmaya_filename).to_os_specific() << "!\n";
342  exit(1);
343  }
344 
345  // Re-set MAYA_LOCATION to its properly sanitized form.
346  {
347  string putenv_str = "MAYA_LOCATION=" + maya_location.to_os_specific();
348  char *putenv_cstr = strdup(putenv_str.c_str());
349  putenv(putenv_cstr);
350  }
351 
352 #ifdef WIN32
353  string sep = ";";
354 #else
355  string sep = ":";
356 #endif
357 
358  // Now set PYTHONHOME & PYTHONPATH. Maya2008 requires this to be set and
359  // pointing within $MAYA_LOCATION, or it might get itself confused with
360  // another Python installation (e.g. Panda's).
361  Filename python = Filename(maya_location, "Python");
362  if (python.is_directory()) {
363  {
364  string putenv_str = "PYTHONHOME=" + python.to_os_specific();
365  char *putenv_cstr = strdup(putenv_str.c_str());
366  putenv(putenv_cstr);
367  }
368  {
369  string putenv_str = "PYTHONPATH=" + python.to_os_specific();
370 
371  Filename pyzip = find_pyzip(maya_location);
372  if (!pyzip.empty() && pyzip.exists()) {
373  putenv_str += sep;
374  putenv_str += pyzip.to_os_specific();
375  }
376 
377  Filename site_packages(python, "lib/site-packages");
378  if (site_packages.is_directory()) {
379  putenv_str += sep;
380  putenv_str += site_packages.to_os_specific();
381  }
382 
383  char *putenv_cstr = strdup(putenv_str.c_str());
384  putenv(putenv_cstr);
385  }
386  }
387 
388  // Also put the Maya bin directory on the PATH.
389 #ifdef IS_OSX
390  Filename bin = Filename(maya_location, "MacOS");
391 #else
392  Filename bin = Filename(maya_location, "bin");
393 #endif
394  if (bin.is_directory()) {
395  const char *path = getenv("PATH");
396  if (path == nullptr) {
397  path = "";
398  }
399  string putenv_str = "PATH=" + bin.to_os_specific() + sep + path;
400  char *putenv_cstr = strdup(putenv_str.c_str());
401  putenv(putenv_cstr);
402  }
403 
404 #ifdef IS_OSX
405  // And on DYLD_LIBRARY_PATH.
406  if (bin.is_directory()) {
407  const char *path = getenv("DYLD_LIBRARY_PATH");
408  if (path == nullptr) {
409  path = "";
410  }
411  string sep = ":";
412  string putenv_str = "DYLD_LIBRARY_PATH=" + bin.to_os_specific() + sep + path;
413  char *putenv_cstr = strdup(putenv_str.c_str());
414  putenv(putenv_cstr);
415  }
416 
417  // We also have to give it a way to find the Maya frameworks.
418  Filename fw_dir = Filename(maya_location, "Frameworks");
419  if (fw_dir.is_directory()) {
420  const char *path = getenv("DYLD_FALLBACK_FRAMEWORK_PATH");
421  if (path == nullptr) {
422  path = "";
423  }
424  string sep = ":";
425  string putenv_str = "DYLD_FALLBACK_FRAMEWORK_PATH=" + fw_dir.to_os_specific() + sep + path;
426  char *putenv_cstr = strdup(putenv_str.c_str());
427  putenv(putenv_cstr);
428  }
429 
430 #elif !defined(_WIN32)
431  // Linux (or other non-Windows OS) gets it added to LD_LIBRARY_PATH.
432  if (bin.is_directory()) {
433  const char *path = getenv("LD_LIBRARY_PATH");
434  if (path == nullptr) {
435  path = "";
436  }
437  string sep = ":";
438  string putenv_str = "LD_LIBRARY_PATH=" + bin.to_os_specific() + sep + path;
439  char *putenv_cstr = strdup(putenv_str.c_str());
440  putenv(putenv_cstr);
441  }
442 
443 #endif // IS_OSX
444 
445  // When this is set, Panda3D will try not to use any functions from the
446  // CPython API. This is necessary because Maya links with its own copy of
447  // Python, which may be incompatible with ours.
448  putenv((char *)"PANDA_INCOMPATIBLE_PYTHON=1");
449 
450  // Now that we have set up the environment variables properly, chain to the
451  // actual maya2egg_bin (or whichever) executable.
452 
453 #ifdef _WIN32
454  // Windows case.
455  char *command_line = strdup(GetCommandLine());
456  STARTUPINFO startup_info;
457  PROCESS_INFORMATION process_info;
458  GetStartupInfo(&startup_info);
459  BOOL result = CreateProcess(os_command.c_str(),
460  command_line,
461  nullptr, nullptr, true, 0,
462  nullptr, nullptr,
463  &startup_info,
464  &process_info);
465  if (result) {
466  WaitForSingleObject(process_info.hProcess, INFINITE);
467  DWORD exit_code = 0;
468 
469  if (GetExitCodeProcess(process_info.hProcess, &exit_code)) {
470  if (exit_code != 0) {
471  cerr << "Program exited with status " << exit_code << "\n";
472  }
473  }
474 
475  CloseHandle(process_info.hProcess);
476  CloseHandle(process_info.hThread);
477  exit(exit_code);
478  }
479  cerr << "Couldn't execute " << command << ": " << GetLastError() << "\n";
480 
481 #else
482  // Unix case.
483  execvp(os_command.c_str(), argv);
484 #endif
485 
486  // Couldn't execute for some reason.
487  return 1;
488 }
void set_extension(const std::string &s)
Replaces the file extension.
Definition: filename.cxx:804
static Filename expand_from(const std::string &user_string, Type type=T_general)
Returns the same thing as from_os_specific(), but embedded environment variable references (e....
Definition: filename.cxx:407
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool resolve_filename(const DSearchPath &searchpath, const std::string &default_extension=std::string())
Searches the given search path for the filename.
Definition: filename.cxx:1581
bool make_canonical()
Converts this filename to a canonical name by replacing the directory part with the fully-qualified d...
Definition: filename.cxx:1011
Stores a 128-bit value that represents the hashed contents (typically MD5) of a file or buffer.
Definition: hashVal.h:31
bool is_fully_qualified() const
Returns true if the filename is fully qualified, e.g.
Definition: filename.I:562
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
std::string get_fullpath() const
Returns the entire filename: directory, basename, extension.
Definition: filename.I:338
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::string to_os_long_name() const
This is the opposite of to_os_short_name(): it returns the "long name" of the filename,...
Definition: filename.cxx:1236
bool is_regular_file() const
Returns true if the filename exists and is the name of a regular file (i.e.
Definition: filename.cxx:1297
std::string get_extension() const
Returns the file extension.
Definition: filename.I:400
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool is_directory() const
Returns true if the filename exists and is a directory name, false otherwise.
Definition: filename.cxx:1359
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::streamsize get_file_size() const
Returns the size of the file in bytes, or 0 if there is an error.
Definition: filename.cxx:1551
This class stores a list of directories that can be searched, in order, to locate a particular file.
Definition: dSearchPath.h:28
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void append_path(const std::string &path, const std::string &separator=std::string())
Adds all of the directories listed in the search path to the end of the search list.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::string to_os_specific() const
Converts the filename from our generic Unix-like convention (forward slashes starting with the root a...
Definition: filename.cxx:1123
bool exists() const
Returns true if the filename exists on the disk, false otherwise.
Definition: filename.cxx:1267
static Filename from_os_specific(const std::string &os_specific, Type type=T_general)
This named constructor returns a Panda-style filename (that is, using forward slashes,...
Definition: filename.cxx:328
This class can be used to test for string matches against standard Unix- shell filename globbing conv...
Definition: globPattern.h:32
get_environment_variable
Returns the definition of the indicated environment variable, or the empty string if the variable is ...