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  { "MAYA2019", "2019"},
110  { "MAYA2020", "2020"},
111  { 0, 0 },
112 };
113 
114 static const char *
115 get_version_number(const char *ver) {
116  for (int i = 0; maya_versions[i].ver != 0; ++i) {
117  if (strcmp(maya_versions[i].ver, ver) == 0) {
118  return maya_versions[i].key;
119  }
120  }
121  return 0;
122 }
123 
124 #if defined(_WIN32)
125 static void
126 get_maya_location(const char *ver, string &loc) {
127  char fullkey[1024];
128  const char *developer;
129  LONG res;
130 
131  for (int dev=0; dev<3; dev++) {
132  switch (dev) {
133  case 0: developer="Alias|Wavefront"; break;
134  case 1: developer="Alias"; break;
135  case 2: developer="Autodesk"; break;
136  }
137  sprintf(fullkey, "SOFTWARE\\%s\\Maya\\%s\\Setup\\InstallPath", developer, ver);
138  for (int hive=0; hive<2; hive++) {
139  HKEY hkey;
140  res = RegOpenKeyEx(HKEY_LOCAL_MACHINE, fullkey, 0, KEY_READ | (hive ? 256:0), &hkey);
141  if (res == ERROR_SUCCESS) {
142  DWORD dtype;
143  DWORD size = 4096;
144  char result[4096 + 1];
145  res = RegQueryValueEx(hkey, "MAYA_INSTALL_LOCATION", nullptr, &dtype, (LPBYTE)result, &size);
146  if ((res == ERROR_SUCCESS)&&(dtype == REG_SZ)) {
147  result[size] = 0;
148  loc = result;
149  }
150  RegCloseKey(hkey);
151  }
152  }
153  }
154 }
155 
156 #elif defined(__APPLE__)
157 static void
158 get_maya_location(const char *ver, string &loc) {
159  char mpath[64];
160  sprintf(mpath, "/Applications/Autodesk/maya%s/Maya.app/Contents", ver);
161  struct stat st;
162  if(stat(mpath, &st) == 0) {
163  loc = mpath;
164  }
165 }
166 
167 #else // _WIN32
168 static void
169 get_maya_location(const char *ver, string &loc) {
170  char mpath[64];
171 #if __WORDSIZE == 64
172  sprintf(mpath, "/usr/autodesk/maya%s-x64", ver);
173 #else
174  sprintf(mpath, "/usr/autodesk/maya%s", ver);
175 #endif
176  struct stat st;
177  if(stat(mpath, &st) == 0) {
178  loc = mpath;
179  } else {
180 #if __WORDSIZE == 64
181  sprintf(mpath, "/usr/aw/maya%s-x64", ver);
182 #else
183  sprintf(mpath, "/usr/aw/maya%s", ver);
184 #endif
185  if(stat(mpath, &st) == 0) {
186  loc = mpath;
187  }
188  }
189 }
190 
191 #endif // _WIN32
192 
193 
194 int
195 main(int argc, char *argv[]) {
196  // First, get the command line and append _bin, so we will actually run
197  // maya2egg_bin.exe, egg2maya_bin.exe, etc.
199 
200  if (command.empty() || command == "unknown" || !command.exists()) {
201  command = Filename::from_os_specific(argv[0]);
202 
203  if (!command.is_fully_qualified()) {
204  DSearchPath path;
206  #ifdef _WIN32
207  command.set_extension("exe");
208  #endif
209  command.resolve_filename(path);
210  }
211  }
212 
213  command.set_basename_wo_extension(command.get_basename_wo_extension() + "_bin");
214  string os_command = command.to_os_specific();
215 
216  // First start with $PANDA_MAYA_LOCATION. If it is set, it overrides
217  // everything else.
218  Filename maya_location = Filename::expand_from("$PANDA_MAYA_LOCATION");
219  if (!maya_location.empty()) {
220  // Reset maya_location to its full long name, because Maya requires this.
221  maya_location.make_canonical();
222  maya_location = Filename::from_os_specific(maya_location.to_os_long_name());
223 
224  } else {
225  // $PANDA_MAYA_LOCATION wasn't set, so check the normal locations. First,
226  // we get the standard location, as a point of reference.
227  Filename standard_maya_location;
228 #ifdef MAYAVERSION
229  const char *key = get_version_number(TOSTRING(MAYAVERSION));
230  if (key == nullptr) {
231  cerr << "Unknown Maya version: " << TOSTRING(MAYAVERSION) << "\n";
232  } else {
233  string loc;
234  get_maya_location(key, loc);
235  if (loc.empty()) {
236  cerr << "Cannot locate " << TOSTRING(MAYAVERSION) << ": it does not appear to be installed.\n";
237  } else {
238  standard_maya_location = Filename::from_os_specific(loc);
239  }
240  }
241  if (!standard_maya_location.empty()) {
242  // Reset standard_maya_location to its full long name, so we can compare
243  // reliably to the given version.
244  standard_maya_location.make_canonical();
245  standard_maya_location = Filename::from_os_specific(standard_maya_location.to_os_long_name());
246  }
247 #endif // MAYAVERSION
248 
249  // Now check if $MAYA_LOCATION is set. If it is, and it's consistent with
250  // the standard location, we respect it.
251  maya_location = Filename::expand_from("$MAYA_LOCATION");
252  if (!maya_location.empty()) {
253  // Reset maya_location to its full long name, so we can compare it
254  // reliably to the standard location; and also because Maya requires
255  // this.
256  maya_location.make_canonical();
257  maya_location = Filename::from_os_specific(maya_location.to_os_long_name());
258  }
259 
260  if (maya_location.empty()) {
261  // If it is not set, we use the standard version instead.
262  maya_location = standard_maya_location;
263 
264  } else if (maya_location != standard_maya_location) {
265  // If it *is* set, we verify that OpenMaya.dll matches the standard
266  // version.
267  Filename openmaya_given = Filename::dso_filename(Filename(maya_location, openmaya_filename));
268  Filename openmaya_standard = Filename::dso_filename(Filename(standard_maya_location, openmaya_filename));
269 
270  if (openmaya_given != openmaya_standard) {
271 #ifdef HAVE_OPENSSL
272  // If we have OpenSSL, we can use it to check the md5 hashes of the
273  // DLL.
274  HashVal hash_given, hash_standard;
275  if (!hash_standard.hash_file(openmaya_standard)) {
276  // Couldn't read the standard file, so use the given one.
277 
278  } else {
279  if (!hash_given.hash_file(openmaya_given)) {
280  // Couldn't even read the given file; use the standard one
281  // instead.
282  maya_location = standard_maya_location;
283 
284  } else {
285  if (hash_standard != hash_given) {
286  // No match; it must be the wrong version.
287  cerr << "$MAYA_LOCATION points to wrong version; using standard location instead.\n";
288  maya_location = standard_maya_location;
289  } else {
290  // The hash matches; keep the given MAYA_LOCATION setting.
291  }
292  }
293  }
294 #else // HAVE_OPENSSL
295  // Without OpenSSL, just check the DLL filesize only.
296  off_t size_given, size_standard;
297  size_standard = openmaya_standard.get_file_size();
298  if (size_standard == 0) {
299  // Couldn't read the standard file, so use the given one.
300 
301  } else {
302  size_given = openmaya_given.get_file_size();
303  if (size_given == 0) {
304  // Couldn't even read the given file; use the standard one
305  // instead.
306  maya_location = standard_maya_location;
307 
308  } else {
309  if (size_standard != size_given) {
310  // No match; it must be the wrong version.
311  cerr << "$MAYA_LOCATION points to wrong version; using standard location instead.\n";
312  maya_location = standard_maya_location;
313 
314  } else {
315  // The size matches; keep the given MAYA_LOCATION setting.
316  }
317  }
318  }
319 
320 #endif // HAVE_OPENSSL
321  }
322  }
323  }
324 
325  if (maya_location.empty()) {
326  cerr << "$MAYA_LOCATION is not set!\n";
327  exit(1);
328  }
329 
330  cerr << "MAYA_LOCATION: " << maya_location.to_os_specific() << endl;
331  if (!maya_location.is_directory()) {
332  cerr << "The directory referred to by $MAYA_LOCATION does not exist!\n";
333  exit(1);
334  }
335 
336  // Look for OpenMaya.dll as a sanity check.
337  Filename openmaya = Filename::dso_filename(Filename(maya_location, openmaya_filename));
338  if (!openmaya.is_regular_file()) {
339  cerr << "Could not find $MAYA_LOCATION/" << Filename::dso_filename(openmaya_filename).to_os_specific() << "!\n";
340  exit(1);
341  }
342 
343  // Re-set MAYA_LOCATION to its properly sanitized form.
344  {
345  string putenv_str = "MAYA_LOCATION=" + maya_location.to_os_specific();
346  char *putenv_cstr = strdup(putenv_str.c_str());
347  putenv(putenv_cstr);
348  }
349 
350 #ifdef WIN32
351  string sep = ";";
352 #else
353  string sep = ":";
354 #endif
355 
356  // Now set PYTHONHOME & PYTHONPATH. Maya2008 requires this to be set and
357  // pointing within $MAYA_LOCATION, or it might get itself confused with
358  // another Python installation (e.g. Panda's).
359  Filename python = Filename(maya_location, "Python");
360  if (python.is_directory()) {
361  {
362  string putenv_str = "PYTHONHOME=" + python.to_os_specific();
363  char *putenv_cstr = strdup(putenv_str.c_str());
364  putenv(putenv_cstr);
365  }
366  {
367  string putenv_str = "PYTHONPATH=" + python.to_os_specific();
368 
369  Filename pyzip = find_pyzip(maya_location);
370  if (!pyzip.empty() && pyzip.exists()) {
371  putenv_str += sep;
372  putenv_str += pyzip.to_os_specific();
373  }
374 
375  Filename site_packages(python, "lib/site-packages");
376  if (site_packages.is_directory()) {
377  putenv_str += sep;
378  putenv_str += site_packages.to_os_specific();
379  }
380 
381  char *putenv_cstr = strdup(putenv_str.c_str());
382  putenv(putenv_cstr);
383  }
384  }
385 
386  // Also put the Maya bin directory on the PATH.
387 #ifdef IS_OSX
388  Filename bin = Filename(maya_location, "MacOS");
389 #else
390  Filename bin = Filename(maya_location, "bin");
391 #endif
392  if (bin.is_directory()) {
393  const char *path = getenv("PATH");
394  if (path == nullptr) {
395  path = "";
396  }
397  string putenv_str = "PATH=" + bin.to_os_specific() + sep + path;
398  char *putenv_cstr = strdup(putenv_str.c_str());
399  putenv(putenv_cstr);
400  }
401 
402 #ifdef IS_OSX
403  // And on DYLD_LIBRARY_PATH.
404  if (bin.is_directory()) {
405  const char *path = getenv("DYLD_LIBRARY_PATH");
406  if (path == nullptr) {
407  path = "";
408  }
409  string sep = ":";
410  string putenv_str = "DYLD_LIBRARY_PATH=" + bin.to_os_specific() + sep + path;
411  char *putenv_cstr = strdup(putenv_str.c_str());
412  putenv(putenv_cstr);
413  }
414 
415  // We also have to give it a way to find the Maya frameworks.
416  Filename fw_dir = Filename(maya_location, "Frameworks");
417  if (fw_dir.is_directory()) {
418  const char *path = getenv("DYLD_FALLBACK_FRAMEWORK_PATH");
419  if (path == nullptr) {
420  path = "";
421  }
422  string sep = ":";
423  string putenv_str = "DYLD_FALLBACK_FRAMEWORK_PATH=" + fw_dir.to_os_specific() + sep + path;
424  char *putenv_cstr = strdup(putenv_str.c_str());
425  putenv(putenv_cstr);
426  }
427 
428 #elif !defined(_WIN32)
429  // Linux (or other non-Windows OS) gets it added to LD_LIBRARY_PATH.
430  if (bin.is_directory()) {
431  const char *path = getenv("LD_LIBRARY_PATH");
432  if (path == nullptr) {
433  path = "";
434  }
435  string sep = ":";
436  string putenv_str = "LD_LIBRARY_PATH=" + bin.to_os_specific() + sep + path;
437  char *putenv_cstr = strdup(putenv_str.c_str());
438  putenv(putenv_cstr);
439  }
440 
441 #endif // IS_OSX
442 
443  // When this is set, Panda3D will try not to use any functions from the
444  // CPython API. This is necessary because Maya links with its own copy of
445  // Python, which may be incompatible with ours.
446  putenv((char *)"PANDA_INCOMPATIBLE_PYTHON=1");
447 
448  // Now that we have set up the environment variables properly, chain to the
449  // actual maya2egg_bin (or whichever) executable.
450 
451 #ifdef _WIN32
452  // Windows case.
453  char *command_line = strdup(GetCommandLine());
454  STARTUPINFO startup_info;
455  PROCESS_INFORMATION process_info;
456  GetStartupInfo(&startup_info);
457  BOOL result = CreateProcess(os_command.c_str(),
458  command_line,
459  nullptr, nullptr, true, 0,
460  nullptr, nullptr,
461  &startup_info,
462  &process_info);
463  if (result) {
464  WaitForSingleObject(process_info.hProcess, INFINITE);
465  DWORD exit_code = 0;
466 
467  if (GetExitCodeProcess(process_info.hProcess, &exit_code)) {
468  if (exit_code != 0) {
469  cerr << "Program exited with status " << exit_code << "\n";
470  }
471  }
472 
473  CloseHandle(process_info.hProcess);
474  CloseHandle(process_info.hThread);
475  exit(exit_code);
476  }
477  cerr << "Couldn't execute " << command << ": " << GetLastError() << "\n";
478 
479 #else
480  // Unix case.
481  execvp(os_command.c_str(), argv);
482 #endif
483 
484  // Couldn't execute for some reason.
485  return 1;
486 }
Filename::is_regular_file
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
Filename::from_os_specific
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
Filename::to_os_specific
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
Filename::exists
bool exists() const
Returns true if the filename exists on the disk, false otherwise.
Definition: filename.cxx:1267
Filename::resolve_filename
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
Filename::make_canonical
bool make_canonical()
Converts this filename to a canonical name by replacing the directory part with the fully-qualified d...
Definition: filename.cxx:1011
Filename::get_basename_wo_extension
std::string get_basename_wo_extension() const
Returns the basename part of the filename, without the file extension.
Definition: filename.I:386
Filename::expand_from
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
filename.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
hashVal.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
DSearchPath
This class stores a list of directories that can be searched, in order, to locate a particular file.
Definition: dSearchPath.h:28
Filename::to_os_long_name
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
Filename::get_file_size
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
Filename::is_directory
bool is_directory() const
Returns true if the filename exists and is a directory name, false otherwise.
Definition: filename.cxx:1359
dtoolbase.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
executionEnvironment.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
globPattern.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Filename::set_extension
void set_extension(const std::string &s)
Replaces the file extension.
Definition: filename.cxx:804
HashVal
Stores a 128-bit value that represents the hashed contents (typically MD5) of a file or buffer.
Definition: hashVal.h:31
Filename::set_basename_wo_extension
void set_basename_wo_extension(const std::string &s)
Replaces the basename part of the filename, without the file extension.
Definition: filename.cxx:783
dSearchPath.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
DSearchPath::append_path
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.
Definition: dSearchPath.cxx:164
ExecutionEnvironment::get_binary_name
get_binary_name
Returns the name of the binary executable that started this program, if it can be determined.
Definition: executionEnvironment.h:59
GlobPattern
This class can be used to test for string matches against standard Unix- shell filename globbing conv...
Definition: globPattern.h:32
Filename
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
ExecutionEnvironment::get_environment_variable
get_environment_variable
Returns the definition of the indicated environment variable, or the empty string if the variable is ...
Definition: executionEnvironment.h:56
Filename::is_fully_qualified
bool is_fully_qualified() const
Returns true if the filename is fully qualified, e.g.
Definition: filename.I:562