instalar ejecutable crear cocoa osx

crear - Averiguar la ubicación de un archivo ejecutable en Cocoa



py2exe python 3 (5)

¿La ruta de Finder (y, por lo tanto, cualquier aplicación Cocoa iniciada por la GUI) no está configurada desde su shell de inicio de sesión? Si su shell de inicio de sesión y el shell que está utilizando en Terminal.app no ​​son lo mismo, eso probablemente genere cierta confusión.

Esta información puede ser útil: http://lists.apple.com/archives/cocoa-dev/2005/Oct/msg00528.html

Aparentemente, la forma "correcta" de establecer las variables de entorno para los procesos de GUI se encuentra en un archivo .plist oculto. Estoy seguro de que supe esto en un punto, luego lo olvidé rápidamente.

La pregunta simple es: cómo averiguar la ubicación de un archivo ejecutable en una aplicación Cocoa.

Recuerde que, en muchos sistemas operativos tipo Unix, las personas usan el entorno PATH para asignar la ubicación preferida para sus ejecutables, especialmente cuando tienen varias versiones de la misma aplicación en su sistema. Como buena práctica, nuestra aplicación Cocoa debe encontrar la ubicación PREFERIDA del archivo ejecutable que necesita.

Por ejemplo, había un SVN 1.4 en la configuración predeterminada de Leopard en / usr / bin, e instaló una versión mucho más nueva, digamos SVN 1.5.3 a través de MacPorts en / opt / local / bin. Y establece su RUTA usando /etc/path.d o .bash_profile o .zshrc así:

export PATH = / opt / local / bin: $ PATH

Entonces puede usar la nueva versión de svn en lugar de la anterior del sistema. Funciona bien en cualquier entorno terminal. Pero no en las aplicaciones de Cocoa. La aplicación Cocoa, hasta donde yo sé, solo tiene un entorno PATH predeterminado como este:

export PATH = "/ usr / bin: / bin: / usr / sbin: / sbin"

Por defecto, no usará la configuración en /etc/path.d, .bash_profile, .profile, .zshrc, etc.

Entonces, ¿cómo podemos hacer exactamente?

ps Tenemos una semi-solución aquí , pero no puede cumplir plenamente el objetivo para esta pregunta.


La parte difícil de tratar de hacer esto es el hecho de que el usuario puede tener su shell configurado para cualquier cosa: sh, bash, csh, tcsh, y así sucesivamente, y cada shell configura su entorno de terminal de manera diferente. No estoy seguro de si me tomaría la molestia por esto, pero si realmente lo desea, esta es la ruta que tomaría.

El primer paso es descubrir el shell del usuario. En OS X, esta información se almacena en Servicios de directorio, que se puede acceder a través de las API en DirectoryService.framework o mediante la herramienta de línea de comandos dscl . La API de DirectoryService es un dolor real en el culo, así que probablemente iría a la ruta CLI. En Cocoa, puede usar NSTask para ejecutar la herramienta con argumentos para obtener el shell del usuario (dejaré los detalles de esto en otro lugar). El comando se vería así:

dscl -plist localhost -read /Local/Default/Users/username UserShell

Esto devolverá texto XML que puede interpretar como un plist y transformarlo en un NSDictionary, o puede omitir la opción -plist y analizar el resultado textual usted mismo.

Una vez que conozca la ruta al shell del usuario, el siguiente paso sería ejecutar ese shell e indicarle que ejecute el comando env para imprimir el entorno del usuario. Parece que la mayoría de las shells aceptan una opción de línea de comando -c que te permite pasar una cadena para ejecutarla. Supongo que tendrás que asumir que es la interfaz común para cualquier shell que el usuario haya elegido.

Una vez que tenga el entorno del usuario, puede tomar su lista de rutas y buscar el ejecutable que esté buscando. Como dije, realmente no sé si esto vale la pena, pero esa es la dirección que tomaría si estuviera implementando esto.


¿Cuán probable es que sus usuarios tengan versiones personalizadas de la herramienta que está utilizando (y qué tan probable es que su aplicación sea compatible con versiones arbitrarias de la herramienta)? Si la respuesta es "no muy", considere usar la ruta de acceso a la herramienta suministrada por el sistema de forma predeterminada, y ofrezca a los usuarios avanzados una forma de especificar su propia ruta como preferencia.


Relacionado con la respuesta de Brian Webster:

Una forma más fácil de obtener el shell del usuario es usar la clase NSProcessInfo. p.ej

NSDictionary *environmentDict = [[NSProcessInfo processInfo] environment]; NSString *shellString = [environmentDict objectForKey:@"SHELL"];

Lo cual es más fácil que usar dscl y analizar la entrada XML.


Aquí está mi implementación basada en las respuestas anteriores, que se llamará desde applicationDidFinishLaunching:

// from http://cocoawithlove.com/2009/05/invoking-other-processes-in-cocoa.html #import "NSTask+OneLineTasksWithOutput.h" void FixUnixPath() { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void){ NSString *userShell = [[[NSProcessInfo processInfo] environment] objectForKey:@"SHELL"]; NSLog(@"User''s shell is %@", userShell); // avoid executing stuff like /sbin/nologin as a shell BOOL isValidShell = NO; for (NSString *validShell in [[NSString stringWithContentsOfFile:@"/etc/shells" encoding:NSUTF8StringEncoding error:nil] componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]]) { if ([[validShell stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] isEqualToString:userShell]) { isValidShell = YES; break; } } if (!isValidShell) { NSLog(@"Shell %@ is not in /etc/shells, won''t continue.", userShell); return; } NSString *userPath = [[NSTask stringByLaunchingPath:userShell withArguments:[NSArray arrayWithObjects:@"-c", @"echo $PATH", nil] error:nil] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; if (userPath.length > 0 && [userPath rangeOfString:@":"].length > 0 && [userPath rangeOfString:@"/usr/bin"].length > 0) { // BINGO! NSLog(@"User''s PATH as reported by %@ is %@", userShell, userPath); setenv("PATH", [userPath fileSystemRepresentation], 1); } }); }

PD: La razón por la que esto funciona es porque detecta los cambios de entorno que produce el shell. Por ejemplo, RVM agrega PATH=$PATH:$HOME/.rvm/bin a .bashrc en la instalación. Las aplicaciones Cocoa se lanzan desde launchd, por lo que no tienen estos cambios en su PATH.

No estoy 100% satisfecho con este código, porque no capta todo. Mi intención original era manejar RVM específicamente, así que tuve que usar un shell que no sea de inicio de sesión aquí, pero en la práctica, la gente coloca al azar la modificación de PATH en .bashrc y .bash_profile, por lo que sería mejor ejecutar ambos.

Uno de mis usuarios incluso tenía un menú interactivo (!!!) en su perfil de shell, lo que naturalmente llevó a que este código se colgara y yo exporté una bandera de env de shell solo para él. :-) Agregar un tiempo de espera es probablemente una buena idea.

Esto también asume que el shell es compatible con bourne y por lo tanto no funciona con fish 2.0, que cada vez es más popular entre la comunidad de hackers. (Fish considera $ PATH una matriz, no una cadena delimitada por dos puntos. Y así la imprime usando espacios como delimitadores por defecto. Uno probablemente puede cocinar una solución fácil, como correr for i in $PATH; echo "PATH=$i"; end y luego solo tomar las líneas que comienzan con PATH= . El filtrado es una buena idea en cualquier caso, porque los scripts de perfil a menudo imprimen algo por sí mismos).

Como nota final, este código ha sido una parte importante de una aplicación de envío durante más de un año (la herramienta de desarrollador de pago top 10 en la Mac App Store durante la mayor parte del año). Sin embargo, ahora estoy implementando sandboxing y quitándolo; naturalmente, no puedes hacer este truco desde una aplicación de espacio aislado. Lo estoy reemplazando con soporte explícito para RVM y amigos, y reproduciendo sus respectivos cambios de entorno manualmente.

Para aquellos que deseen usar algo como el sistema Git desde una aplicación de espacio aislado, tenga en cuenta que si bien no tiene acceso para leer archivos y enumerar directorios, tiene acceso a stat - [[NSFileManager defaultManager] fileExistsAtPath:path] . Puede usar esto para explorar una lista codificada de carpetas típicas que buscan su binario, y cuando encuentre las ubicaciones (como / usr / local o / opt / local o lo que sea), solicite al usuario que le brinde acceso a través de NSOpenPanel. Esto no afectará a todos los casos, pero manejará el 90% de los casos de uso y es lo mejor que puede hacer para sus usuarios de inmediato.