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 }
This class stores a list of directories that can be searched, in order, to locate a particular file.
Definition: dSearchPath.h:28
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.
get_environment_variable
Returns the definition of the indicated environment variable, or the empty string if the variable is ...
get_binary_name
Returns the name of the binary executable that started this program, if it can be determined.
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
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
bool is_fully_qualified() const
Returns true if the filename is fully qualified, e.g.
Definition: filename.I:562
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
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
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
void set_basename_wo_extension(const std::string &s)
Replaces the basename part of the filename, without the file extension.
Definition: filename.cxx:783
bool make_canonical()
Converts this filename to a canonical name by replacing the directory part with the fully-qualified d...
Definition: filename.cxx:1011
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
void set_extension(const std::string &s)
Replaces the file extension.
Definition: filename.cxx:804
bool is_directory() const
Returns true if the filename exists and is a directory name, false otherwise.
Definition: filename.cxx:1359
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
std::string get_basename_wo_extension() const
Returns the basename part of the filename, without the file extension.
Definition: filename.I:386
bool exists() const
Returns true if the filename exists on the disk, false otherwise.
Definition: filename.cxx:1267
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 can be used to test for string matches against standard Unix- shell filename globbing conv...
Definition: globPattern.h:32
Stores a 128-bit value that represents the hashed contents (typically MD5) of a file or buffer.
Definition: hashVal.h:31
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.