Panda3D
Loading...
Searching...
No Matches
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"
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
54using std::cerr;
55using std::endl;
56using std::string;
57
58#define QUOTESTR(x) #x
59#define TOSTRING(x) QUOTESTR(x)
60
61// Searches for python26.zip or whatever version it is.
62static Filename
63find_pyzip(const Filename &maya_location) {
64 // This is where python26.zip appears on Windows. Should it be in other
65 // locations on other platforms?
66 Filename dirname(maya_location, "bin");
67
68 vector_string results;
69 GlobPattern glob("python*.zip");
70 if (glob.match_files(results, dirname) != 0) {
71 return Filename(dirname, results[0]);
72 }
73
74 return Filename();
75}
76
77struct MayaVerInfo {
78 const char *ver, *key;
79};
80
81struct MayaVerInfo maya_versions[] = {
82 { "MAYA6", "6.0" },
83 { "MAYA65", "6.5" },
84 { "MAYA7", "7.0" },
85 { "MAYA8", "8.0" },
86 { "MAYA85", "8.5" },
87 { "MAYA2008", "2008"},
88 { "MAYA2009", "2009"},
89 { "MAYA2010", "2010"},
90 { "MAYA2011", "2011"},
91 { "MAYA2012", "2012"},
92 { "MAYA2013", "2013"},
93 { "MAYA20135", "2013.5"},
94 { "MAYA2014", "2014"},
95 { "MAYA2015", "2015"},
96 { "MAYA2016", "2016"},
97 { "MAYA20165", "2016.5"},
98 { "MAYA2017", "2017"},
99 { "MAYA2018", "2018"},
100 { "MAYA2019", "2019"},
101 { "MAYA2020", "2020"},
102 { "MAYA2022", "2022"},
103 { 0, 0 },
104};
105
106static const char *
107get_version_number(const char *ver) {
108 for (int i = 0; maya_versions[i].ver != 0; ++i) {
109 if (strcmp(maya_versions[i].ver, ver) == 0) {
110 return maya_versions[i].key;
111 }
112 }
113 return 0;
114}
115
116static Filename
117get_openmaya_filename(const Filename &maya_location) {
118#ifdef _WIN32
119 // Note: Filename::dso_filename changes .so to .dll automatically.
120 // Maya 2022 has two versions of OpenMaya.dll, one for Python 3 and
121 // one for Python 2, in bin3 and bin2 folders.
122 Filename bin3 = Filename(maya_location, "bin3");
123 Filename bin3_openmaya = Filename::dso_filename(maya_location / "bin3/OpenMaya.so");
124 if (bin3_openmaya.is_regular_file()) {
125 return bin3_openmaya;
126 }
127 return Filename::dso_filename(maya_location / "bin/OpenMaya.so");
128#elif defined(IS_OSX)
129 return Filename::dso_filename(maya_location / "MacOS/libOpenMaya.dylib");
130#else
131 return Filename::dso_filename(maya_location / "lib/libOpenMaya.so");
132#endif // _WIN32
133}
134
135#if defined(_WIN32)
136static void
137get_maya_location(const char *ver, string &loc) {
138 char fullkey[1024];
139 const char *developer;
140 LONG res;
141
142 for (int dev=0; dev<3; dev++) {
143 switch (dev) {
144 case 0: developer="Alias|Wavefront"; break;
145 case 1: developer="Alias"; break;
146 case 2: developer="Autodesk"; break;
147 }
148 sprintf(fullkey, "SOFTWARE\\%s\\Maya\\%s\\Setup\\InstallPath", developer, ver);
149 for (int hive=0; hive<2; hive++) {
150 HKEY hkey;
151 res = RegOpenKeyEx(HKEY_LOCAL_MACHINE, fullkey, 0, KEY_READ | (hive ? 256:0), &hkey);
152 if (res == ERROR_SUCCESS) {
153 DWORD dtype;
154 DWORD size = 4096;
155 char result[4096 + 1];
156 res = RegQueryValueEx(hkey, "MAYA_INSTALL_LOCATION", nullptr, &dtype, (LPBYTE)result, &size);
157 if ((res == ERROR_SUCCESS)&&(dtype == REG_SZ)) {
158 result[size] = 0;
159 loc = result;
160 }
161 RegCloseKey(hkey);
162 }
163 }
164 }
165}
166
167#elif defined(__APPLE__)
168static void
169get_maya_location(const char *ver, string &loc) {
170 char mpath[64];
171 sprintf(mpath, "/Applications/Autodesk/maya%s/Maya.app/Contents", ver);
172 struct stat st;
173 if(stat(mpath, &st) == 0) {
174 loc = mpath;
175 }
176}
177
178#else // _WIN32
179static void
180get_maya_location(const char *ver, string &loc) {
181 char mpath[64];
182#if __WORDSIZE == 64
183 sprintf(mpath, "/usr/autodesk/maya%s-x64", ver);
184#else
185 sprintf(mpath, "/usr/autodesk/maya%s", ver);
186#endif
187 struct stat st;
188 if(stat(mpath, &st) == 0) {
189 loc = mpath;
190 } else {
191#if __WORDSIZE == 64
192 sprintf(mpath, "/usr/aw/maya%s-x64", ver);
193#else
194 sprintf(mpath, "/usr/aw/maya%s", ver);
195#endif
196 if(stat(mpath, &st) == 0) {
197 loc = mpath;
198 }
199 }
200}
201
202#endif // _WIN32
203
204
205int
206main(int argc, char *argv[]) {
207 // First, get the command line and append _bin, so we will actually run
208 // maya2egg_bin.exe, egg2maya_bin.exe, etc.
210
211 if (command.empty() || command == "unknown" || !command.exists()) {
212 command = Filename::from_os_specific(argv[0]);
213
214 if (!command.is_fully_qualified()) {
215 DSearchPath path;
217 #ifdef _WIN32
218 command.set_extension("exe");
219 #endif
220 command.resolve_filename(path);
221 }
222 }
223
224 command.set_basename_wo_extension(command.get_basename_wo_extension() + "_bin");
225 string os_command = command.to_os_specific();
226
227 // First start with $PANDA_MAYA_LOCATION. If it is set, it overrides
228 // everything else.
229 Filename maya_location = Filename::expand_from("$PANDA_MAYA_LOCATION");
230 if (!maya_location.empty()) {
231 // Reset maya_location to its full long name, because Maya requires this.
232 maya_location.make_canonical();
233 maya_location = Filename::from_os_specific(maya_location.to_os_long_name());
234
235 } else {
236 // $PANDA_MAYA_LOCATION wasn't set, so check the normal locations. First,
237 // we get the standard location, as a point of reference.
238 Filename standard_maya_location;
239#ifdef MAYAVERSION
240 const char *key = get_version_number(TOSTRING(MAYAVERSION));
241 if (key == nullptr) {
242 cerr << "Unknown Maya version: " << TOSTRING(MAYAVERSION) << "\n";
243 } else {
244 string loc;
245 get_maya_location(key, loc);
246 if (loc.empty()) {
247 cerr << "Cannot locate " << TOSTRING(MAYAVERSION) << ": it does not appear to be installed.\n";
248 } else {
249 standard_maya_location = Filename::from_os_specific(loc);
250 }
251 }
252 if (!standard_maya_location.empty()) {
253 // Reset standard_maya_location to its full long name, so we can compare
254 // reliably to the given version.
255 standard_maya_location.make_canonical();
256 standard_maya_location = Filename::from_os_specific(standard_maya_location.to_os_long_name());
257 }
258#endif // MAYAVERSION
259
260 // Now check if $MAYA_LOCATION is set. If it is, and it's consistent with
261 // the standard location, we respect it.
262 maya_location = Filename::expand_from("$MAYA_LOCATION");
263 if (!maya_location.empty()) {
264 // Reset maya_location to its full long name, so we can compare it
265 // reliably to the standard location; and also because Maya requires
266 // this.
267 maya_location.make_canonical();
268 maya_location = Filename::from_os_specific(maya_location.to_os_long_name());
269 }
270
271 if (maya_location.empty()) {
272 // If it is not set, we use the standard version instead.
273 maya_location = standard_maya_location;
274
275 } else if (maya_location != standard_maya_location) {
276 // If it *is* set, we verify that OpenMaya.dll matches the standard
277 // version.
278 Filename openmaya_given = get_openmaya_filename(maya_location);
279 Filename openmaya_standard = get_openmaya_filename(standard_maya_location);
280
281 if (openmaya_given != openmaya_standard) {
282#ifdef HAVE_OPENSSL
283 // If we have OpenSSL, we can use it to check the md5 hashes of the
284 // DLL.
285 HashVal hash_given, hash_standard;
286 if (!hash_standard.hash_file(openmaya_standard)) {
287 // Couldn't read the standard file, so use the given one.
288
289 } else {
290 if (!hash_given.hash_file(openmaya_given)) {
291 // Couldn't even read the given file; use the standard one
292 // instead.
293 maya_location = standard_maya_location;
294
295 } else {
296 if (hash_standard != hash_given) {
297 // No match; it must be the wrong version.
298 cerr << "$MAYA_LOCATION points to wrong version; using standard location instead.\n";
299 maya_location = standard_maya_location;
300 } else {
301 // The hash matches; keep the given MAYA_LOCATION setting.
302 }
303 }
304 }
305#else // HAVE_OPENSSL
306 // Without OpenSSL, just check the DLL filesize only.
307 off_t size_given, size_standard;
308 size_standard = openmaya_standard.get_file_size();
309 if (size_standard == 0) {
310 // Couldn't read the standard file, so use the given one.
311
312 } else {
313 size_given = openmaya_given.get_file_size();
314 if (size_given == 0) {
315 // Couldn't even read the given file; use the standard one
316 // instead.
317 maya_location = standard_maya_location;
318
319 } else {
320 if (size_standard != size_given) {
321 // No match; it must be the wrong version.
322 cerr << "$MAYA_LOCATION points to wrong version; using standard location instead.\n";
323 maya_location = standard_maya_location;
324
325 } else {
326 // The size matches; keep the given MAYA_LOCATION setting.
327 }
328 }
329 }
330
331#endif // HAVE_OPENSSL
332 }
333 }
334 }
335
336 if (maya_location.empty()) {
337 cerr << "$MAYA_LOCATION is not set!\n";
338 exit(1);
339 }
340
341 cerr << "MAYA_LOCATION: " << maya_location.to_os_specific() << endl;
342 if (!maya_location.is_directory()) {
343 cerr << "The directory referred to by $MAYA_LOCATION does not exist!\n";
344 exit(1);
345 }
346
347 // Look for OpenMaya.dll as a sanity check.
348 Filename openmaya = get_openmaya_filename(maya_location);
349 if (!openmaya.is_regular_file()) {
350 cerr << "Could not find OpenMaya library in $MAYA_LOCATION!\n";
351 exit(1);
352 }
353
354 // Re-set MAYA_LOCATION to its properly sanitized form.
355 {
356 string putenv_str = "MAYA_LOCATION=" + maya_location.to_os_specific();
357 char *putenv_cstr = strdup(putenv_str.c_str());
358 putenv(putenv_cstr);
359 }
360
361#ifdef WIN32
362 string sep = ";";
363#else
364 string sep = ":";
365#endif
366
367 // Now set PYTHONHOME & PYTHONPATH. Maya2008 requires this to be set and
368 // pointing within $MAYA_LOCATION, or it might get itself confused with
369 // another Python installation (e.g. Panda's).
370 Filename python = Filename(maya_location, "Python");
371 if (python.is_directory()) {
372 {
373 string putenv_str = "PYTHONHOME=" + python.to_os_specific();
374 char *putenv_cstr = strdup(putenv_str.c_str());
375 putenv(putenv_cstr);
376 }
377 {
378 string putenv_str = "PYTHONPATH=" + python.to_os_specific();
379
380 Filename pyzip = find_pyzip(maya_location);
381 if (!pyzip.empty() && pyzip.exists()) {
382 putenv_str += sep;
383 putenv_str += pyzip.to_os_specific();
384 }
385
386 Filename site_packages(python, "lib/site-packages");
387 if (site_packages.is_directory()) {
388 putenv_str += sep;
389 putenv_str += site_packages.to_os_specific();
390 }
391
392 char *putenv_cstr = strdup(putenv_str.c_str());
393 putenv(putenv_cstr);
394 }
395 }
396
397 // Also put the Maya bin directory on the PATH.
398#ifdef IS_OSX
399 Filename bin = Filename(maya_location, "MacOS");
400#else
401 Filename bin = Filename(maya_location, "bin");
402#endif
403 if (bin.is_directory()) {
404 const char *path = getenv("PATH");
405 if (path == nullptr) {
406 path = "";
407 }
408 string putenv_str = "PATH=";
409
410 // On Windows, there may also be a bin3 or bin2 directory, we should
411 // add either one to the PATH.
412#ifdef _WIN32
413 Filename bin3 = Filename(maya_location, "bin3");
414 if (bin3.is_directory()) {
415 putenv_str += bin3.to_os_specific() + sep;
416 }
417#endif
418 putenv_str += bin.to_os_specific() + sep + path;
419
420 char *putenv_cstr = strdup(putenv_str.c_str());
421 putenv(putenv_cstr);
422 }
423
424#ifdef IS_OSX
425 // And on DYLD_LIBRARY_PATH.
426 if (bin.is_directory()) {
427 const char *path = getenv("DYLD_LIBRARY_PATH");
428 if (path == nullptr) {
429 path = "";
430 }
431 string sep = ":";
432 string putenv_str = "DYLD_LIBRARY_PATH=" + bin.to_os_specific() + sep + path;
433 char *putenv_cstr = strdup(putenv_str.c_str());
434 putenv(putenv_cstr);
435 }
436
437 // We also have to give it a way to find the Maya frameworks.
438 Filename fw_dir = Filename(maya_location, "Frameworks");
439 if (fw_dir.is_directory()) {
440 const char *path = getenv("DYLD_FALLBACK_FRAMEWORK_PATH");
441 if (path == nullptr) {
442 path = "";
443 }
444 string sep = ":";
445 string putenv_str = "DYLD_FALLBACK_FRAMEWORK_PATH=" + fw_dir.to_os_specific() + sep + path;
446 char *putenv_cstr = strdup(putenv_str.c_str());
447 putenv(putenv_cstr);
448 }
449
450#elif !defined(_WIN32)
451 // Linux (or other non-Windows OS) gets it added to LD_LIBRARY_PATH.
452 if (bin.is_directory()) {
453 const char *path = getenv("LD_LIBRARY_PATH");
454 if (path == nullptr) {
455 path = "";
456 }
457 string sep = ":";
458 string putenv_str = "LD_LIBRARY_PATH=" + bin.to_os_specific() + sep + path;
459 char *putenv_cstr = strdup(putenv_str.c_str());
460 putenv(putenv_cstr);
461 }
462
463#endif // IS_OSX
464
465 // When this is set, Panda3D will try not to use any functions from the
466 // CPython API. This is necessary because Maya links with its own copy of
467 // Python, which may be incompatible with ours.
468 putenv((char *)"PANDA_INCOMPATIBLE_PYTHON=1");
469
470 // Now that we have set up the environment variables properly, chain to the
471 // actual maya2egg_bin (or whichever) executable.
472
473#ifdef _WIN32
474 // Windows case.
475 char *command_line = strdup(GetCommandLine());
476 STARTUPINFO startup_info;
477 PROCESS_INFORMATION process_info;
478 GetStartupInfo(&startup_info);
479 BOOL result = CreateProcess(os_command.c_str(),
480 command_line,
481 nullptr, nullptr, true, 0,
482 nullptr, nullptr,
483 &startup_info,
484 &process_info);
485 if (result) {
486 WaitForSingleObject(process_info.hProcess, INFINITE);
487 DWORD exit_code = 0;
488
489 if (GetExitCodeProcess(process_info.hProcess, &exit_code)) {
490 if (exit_code != 0) {
491 cerr << "Program exited with status " << exit_code << "\n";
492 }
493 }
494
495 CloseHandle(process_info.hProcess);
496 CloseHandle(process_info.hThread);
497 exit(exit_code);
498 }
499 cerr << "Couldn't execute " << command << ": " << GetLastError() << "\n";
500
501#else
502 // Unix case.
503 execvp(os_command.c_str(), argv);
504#endif
505
506 // Couldn't execute for some reason.
507 return 1;
508}
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:44
bool is_regular_file() const
Returns true if the filename exists on the physical disk and is the name of a regular file (i....
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.
std::string to_os_specific() const
Converts the filename from our generic Unix-like convention (forward slashes starting with the root a...
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...
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 on the physical disk and is a directory name, false otherwise.
std::string to_os_long_name() const
This is the opposite of to_os_short_name(): it returns the "long name" of the filename,...
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 physical disk, false otherwise.
std::streamsize get_file_size() const
Returns the size of the file in bytes, or 0 if there is an error.
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.