How to package a standalone xulrunner app for OS X? - macos

I'm trying to create a xulrunner app for OS X 10.9+. I need it to be standalone, i.e. to not require any extra additional software (including Firefox) to be installed on the box along with the app.
I was not able to google up an up-to-date guide on how to do that. It seems that I've hit every issue described here: https://bugzilla.mozilla.org/show_bug.cgi?id=923979
One of the last ones is:
$ open MyApp.app
LSOpenURLsWithRole() failed with error -10810 for the file /Path/To/MyApp.app.
Here is an what I have so far:
https://drive.google.com/file/d/0BxRquYs2Nx92ZTZaVjk0QThMN2c/view?usp=sharing
How can I create an OS X .app for a modern (v36+) xulrunner application?

You are on the right track, there is just one major issue you are facing.
If you were to run you application by the command line, you would get some output like this.
$ SampleApplication.app/Contents/MacOS/xulrunner
Mozilla XULRunner 33.0
Usage: xulrunner [OPTIONS]
xulrunner APP-FILE [APP-OPTIONS...]
OPTIONS
--app specify APP-FILE (optional)
-h, --help show this message
-v, --version show version
--gre-version print the GRE version string on stdout
APP-FILE
Application initialization file.
APP-OPTIONS
Application specific options
As we can see, the executable did not automatically run the Contents/Resources/application.ini like the tutorials say it will. This is a known issue, and the popular workaround among XULRunner users is to create a stub shell script to pass the required argument to the xulrunner binary.
Here is a script I've whipped up to do just that.
#!/bin/sh
runpath="`dirname "$0"`"
contentsdir="`cd "$runpath"/.. > /dev/null && pwd`"
exec "$runpath/xulrunner" --app "$contentsdir/Resources/application.ini"
Save this text to a file in your MacOS directory and give it executable permissions. For sake of example, I will use sampleapplication. Here is the command to set executable permissions.
chmod +x sampleapplication
Now, modify your Info.plist to execute this script instead of executing xulrunner directly by setting the CFBundleExecutable entry to match your stub shell script.
<key>CFBundleExecutable</key>
<string>sampleapplication</string>
Now when you run your application, it should work. If you are getting an error saying "The application cannot be opened because its executable is missing", you may want to rename the application bundle, or follow the advice in that question to avoid the caching issue.
Bonus Info
You can remove the Contents/Frameworks/XUL.framework directory, it is no longer used and placing the XUL.framework contents in the MacOS folder is now the correct place to put them.
You should also copy the dependentlibs.list file from the Contents/MacOS to the Contents/Resources directory, although XULRunner 33 seems to be getting on fine without it.

Related

Install Kubectl Plugin on Windows

Question: What are the steps to install a kubectl plugin on Windows?
I have written a plugin standalone binary that I would like to invoke from within kubectl (following the instructions in https://kubernetes.io/docs/tasks/extend-kubectl/kubectl-plugins/)
The documentation for installation states to perform the following steps:
"A plugin is nothing more than a standalone executable file, whose name begins with kubectl-. To install a plugin, simply move this executable file to anywhere on your PATH."
This works fine on Mac and Linux, but performing those instructions on Windows does not seem to work. Running "kubectl plugin list" does not list my plugin and I cannot invoke it from within kubectl. I even tried adding my binary to the .kube directory autogenerated by kubectl, and it does not detect the plugin.
Several discussions on github reference this issue, without providing a response of how to install a kubectl plugin on Windows (ex: https://github.com/kubernetes/kubernetes/issues/73289). And after performing a lengthy google/stackoverflow search, there don't seem to be any tutorials/solutions that I (or my teammates) could locate. Any help would be much appreciated! Thank you.
In my case I don't have an issue with installing a plugin on Windows 10 machine (by simply including it on my PATH). Here is the output of 'kubectl plugin list':
c:\opt\bin>kubectl plugin list
The following kubectl-compatible plugins are available:
c:\opt\bin\kubectl-getbuildver.bat
- warning: c:\opt\bin\kubectl-getbuildver.bat identified as a kubectl plugin, but it is not executable
c:\opt\bin\kubectl-hello.exe
c:\opt\bin\kubectl-helloworld.p6
- warning: c:\opt\bin\kubectl-helloworld.p6 identified as a kubectl plugin, but it is not executable
error: 2 plugin warnings were found
Instead I'm encountering a known github issue: 'not supported by windows' error, while invoking my plugin with kubectl (v1.13.4).
c:\opt\bin>kubectl hello
not supported by windows
c:\opt\bin>kubectl-hello.exe
Tuesday
*kubectl-hello.exe - is console application written in csharp. I tried also to use Windows batch file and Perl6 program as plugins, but none of these worked out on Windows.
I think only .exe file extensions are considered as executables by kubectl when it searches for plugins in the $PATH when running in Windows environment.
I tested by creating a simple HelloWorld App as a single file executable, added it to my system's $PATH and it got picked up and executed correctly.
kubectl krew like brew to manage the kubectl plugin. You can try it. It supports Window.
https://github.com/kubernetes-sigs/krew

How to debug Mac OS X pkg?

I built a Mac OS X bundle Frequon Invaders.app, and it runs fine. The executable was created with Go. Then I packaged it like this:
$ pkgbuild --component 'Frequon Invaders.app' --install-location /Applications FrequonInvaders.pkg
pkgbuild: Adding component at /Users/Dad/Documents/projects/Frequon-Invaders-2.2/installer-macos/Frequon Invaders.app
pkgbuild: Wrote package to FrequonInvaders.pkg
When I open FrequonInvaders.pkg in Finder, I get a "install Frequon Invaders" window that lets me go through the motions of installing it, and the Summary part says "Installation was successful". But when I look in /Applications, it's not there. Indeed none of the files in the bundle were installed.
[Updated] After looking around, I found that the package appears to have been installed right on top of the original place that Frequon Invaders.app was originally built. It seems that the --install-location /Applications was completely ignored!
Question:
How do I use pkgbuild to build a package that is really installed where install-location said to install it?
How to debug Mac OS X pkg?
Debugging .pkg files is tricky because there's no easy way to get verbose output.
sudo installer -pkg my_package.pkg -target / -verbose
This may help understand the step that's failing but it really doesn't help narrow the problem down...
Next, you can use a utility like The Unarchiver to extract the .pkg file. Your scripts will need to be extracted twice by this utility.
Note: If you prefer the command line:
xar -xf my_package.pkg # extract pkg
tar -xf Scripts # extract scripts
From there, you can attempt to troubleshoot what's going wrong with the scripts.
But in my case, the only way I was able to debug the scripts were to run the package over and over echoing debug statements to a file.
For example:
# preinstall
echo "here!" >> /Users/Tom/Desktop/debug.txt
for such simple installers it is always better to use a tool to do the work for you. I usually use the Packages tool
http://s.sudre.free.fr/Software/Packages/about.html
which is free and really really good.

pyinstaller and simplehttpserver on OSX vs WIN

I've made a simple server based on another thread here on SO: Shutting down python TCPServer by custom handler
I wrapped this up into an .exe using pyinstaller like so:
pyinstaller C:\PATH TO FILE\server.py --distpath=\PATH TO FILE\ --onedir --onefile --noconsole
This works wonderfully on Windows (currently testing on Win 7 x64). I can copy this exe to any folder and get a webserver at that location. I did it this way because I am wrapping this up into a process that would be distributed with another application. The users of this application might not have python installed and they might have a browser that blocks the types of things I would like to serve during the application process.
This should also work on osx, but when I try to wrap the same python script with pyinstaller, instead of serving up the current directory, I get a server at my /Users/Username location and not the directory from where the app was run.
My process to get this to work on both Win and OSX was to begin from the python shell and run the scripts there. This works on both platforms. If I use Terminal, cd to the directory where I want to serve files, and run the python script from there, things work fine. But any attempt I make to wrap the script into something executable leads me back to the same result of serving from /Users/Username, so I'm obviously missing something fundamental of how the wrapping is working. I am assuming it has to do with shell scripting and the script being tied to the current user?
I'd like to continue to use pyinstaller for this process since it would be the same process on Windows and OSX, but I cannot seem to get the resulting app to serve files from the directory from which it is run. Any guidance on what is happening, and how to fix it through pyinstaller (or other method) would be greatly appreciated.
#tallforasmurf on the pyinstaller Github pointed out that I missed something in the pyinstaller docs that is very important:
Adapting to being "frozen"
In some apps it is necessary to learn at run-time whether the app is
running "live" (from source) or "frozen" (part of a bundle). For
example, you might have a configuration file that, when running
"live", is found based on a module's file attribute. That won't
work when the code is bundled.
When your application needs access to a data file, for example a
configuration file or an icon image file, you get the path to the file
with the following code:
import sys
import os
...
if getattr(sys, 'frozen', False):
# we are running in a |PyInstaller| bundle
basedir = sys._MEIPASS
else:
# we are running in a normal Python environment
basedir = os.path.dirname(__file__)
The PyInstaller bootloader adds the attribute frozen to the sys
module. If that attribute exists, your script has been launched by the
bootloader. When that is true, sys._MEIPASS (note the underscore in
the name) contains the path to the folder containing your script and
any other files or folders bundled with it. For one-folder mode this
is the distribution folder. For one-file mode it is the temporary
folder created by the bootloader .
When your program was not started by the bootloader, the standard
Python variable file is the full path to the script now executing,
and os.path.dirname() extracts the path to the folder that contains
it.
I was able to os.chdir(basedir) after going up a few levels past the .app bundle directory and properly serve the files in the same directory as the .app bundle.

ImageMagick deployment on Mac OS

I'm trying to deploy ImageMagick with my own software. On windows I've just included all the core dlls with coders dlls at the exe path and it works well.
But on mac os I have troubles with coders. I installed ImageMagick via macports and found it with the help of CMake. CMake does all the job of copying and fixing up all the core libs I've linked against. Then I copied all the coder libs and fixed them up also, but when I start my application it just can't find any coder. So I'd like to know what am I missing there.
Note: if I didn't fix up any paths it works well. It is only my deployment that is in trouble. Maybe I should include some kind of config file?
P.S. I have all ImageMagick libs including coders SOs near the executable in MacOS bundle sub-folder.
How about setting the MAGICK_CODER_MODULE_PATH in your bundle?
see here: http://www.imagemagick.org/script/resources.php
EDIT:
To improve the information:
Originally when embedding IM in our own app bundle we had three problems:
our app and the IM dylibs not finding referenced IM dylibs,
IM not finding its config files,
IM not finding coders (the No Decode Delegate error)
We tried changing the hardcoded paths in the dylibs using the install_name_tool but finally when doing some tests with moving the IM around to different directories and testing
convert -debug configuration
we found out the all three above problems could be solved just by setting and exporting at least these three environment variables in the terminal console before running convert:
DYLD_LIBRARY_PATH
MAGICK_CONFIGURE_PATH
MAGICK_CODER_MODULE_PATH
With this experience, we returned back to our bundle and in the beginning tried to use the Info.plist fiel to set these variables but it didn't seem to work - probably because there were problems with making the paths to IM inside the bundle relative.
Finally we created a simple sh script and put it into our bundle and configured this bundle to run this script instead of the main app:
#!/bin/sh
CURR_DIR="$( cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
IMAGE_MAGICK_PATH=$CURR_DIR/../Resources/ImageMagick
export DYLD_LIBRARY_PATH=$IMAGE_MAGICK_PATH/lib
export MAGICK_CONFIGURE_PATH=$IMAGE_MAGICK_PATH/lib/ImageMagick-6.8.0/config
export MAGICK_CODER_MODULE_PATH=$IMAGE_MAGICK_PATH/lib/ImageMagick-6.8.0/modules-Q16/coders
# run application
exec $CURR_DIR/OurAppName
The key thing to make it working was properly getting the CURR_DIR of the app bundle (thanks to this post).
And as came out of our tests, setting the environment variables this way makes them visible only for this application execution context - i.e. when we started our app using the bundle, opened terminal and typed
env
the above three variables were missing from the output.
Hope this will help others save couple of days of research and pulling hairs out of their heads ;)
I've found a full solution for deploying ImageMagick in a bundle with the help of CMake. If you don't use CMake then #Tomasz's answer will be of help also.
So let's start:
First of all you need to know what and where ImageMagick is trying to locate when it is used from your own code. To find it out you can use MAGICK_DEBUG environmental variable which could be set to those parameters. It really helps when you debug ImageMagick.
Prerequisites:
I assume that you used FIND_PACKAGE and FIXUP_BUNDLE to find ImageMagick and set its binary paths inside the bundle. The only thing left is to deploy coders. Also I assume that you've installed ImageMagick from Mac Ports.
We need to get ImageMagick version string to correctly locate the coders:
STRING(REGEX REPLACE "-.+" "" ImageMagick_SHORT_VERSION ${ImageMagick_VERSION_STRING})
Now ImageMagick_SHORT_VERSION contains full version without any sub versions.
Then we need to copy all the coders to some predefined folder(I've used ImageMagick/coders subfolder under MacOS part of the bundle)
FILE(COPY /opt/local/lib/ImageMagick-${ImageMagick_SHORT_VERSION}/modules-Q16/coders/ DESTINATION ${PATH_TO_YOUR_BUNDLE}/Contents/MacOS/ImageMagick/coders/)
Now we need to fixup all the *.so libs we have, so we list it and pass to fixup_bundle
FILE(GLOB IMAGEMAGICK_CODERS ${PATH_TO_YOUR_BUNDLE}/Contents/MacOS/ImageMagick/coders/*.so)
Now we should update *.la files which accompanies coders *.so. To achieve it I've used script:
INSTALL(SCRIPT LaScript.cmake COMPONENT Runtime)
Script content:
SET(TARGET_BINARY_DIR "${PATH_TO_YOUR_BUNDLE}")
FILE(GLOB IMAGEMAGICK_CODERS_LA ${TARGET_BINARY_DIR}/Contents/MacOS/ImageMagick/coders/*.la)
FOREACH(file ${IMAGEMAGICK_CODERS_LA})
FILE(READ ${file} FILE_CONTENT)
STRING(REGEX REPLACE "dependency_libs='.*'" " " MODIFIED_FILE_CONTENT ${FILE_CONTENT})
STRING(REGEX REPLACE "libdir='.*'" " " MODIFIED_FILE_CONTENT ${MODIFIED_FILE_CONTENT})
FILE(WRITE ${file} ${MODIFIED_FILE_CONTENT})
ENDFOREACH()
We almost ready the only thing left to be done is to change the way we launch the application. But let's digress a little bit and find out where ImageMagick searches for the coders:
It tries to get the content of MAGICK_CODER_MODULE_PATH environmental variable
Then it checks if MAGICKCORE_CODER_PATH macro is defined(and in fact it does!) and use its value.
Then it will try to use MAGICK_HOME environmental variable and MAGICKCORE_CODER_RELATIVE_PATH to get path to the modules but we don't care since we will stop on #2 anyway!(NOTE: that it is true for Mac Ports installation)
So the only way we can interfere with search is to set MAGICK_CODER_MODULE_PATH environmental variable(Well we can also edit libMagickCodre and replace MAGICKCORE_CODER_PATH with some static path we need but it is too britle way to do things and it won't save us if someone set MAGICK_CODER_MODULE_PATH anyway)
We shouldn't set it system wide since we can break some user installtion so we have 2 options:
Use LSEnvironment to set the MAGICK_CODER_MODULE_PATH to some predefined location
Use script to launch our app and set this variable inside it.
I've chose the later since it is more flexible,
I have the following script:
#!/bin/bash
working_dir="${0%/*}"
export MAGICK_CODER_MODULE_PATH=$working_dir/ImageMagick/coders
executable="${working_dir}/ApplicationName"
"$executable"
and set CFBundleExecutable to the name of the script.
That's all and I hope it will help someone to save his/her time.
You should follow the Mac OS X-specific Build instructions but specifying --enable-shared in the configure options (see this document for details).
I guess that your application can't find the codecs because they have been statically linked to ImageMagick tools. This is usually done to address portability issues. To make codecs available in your application, you should build them as shared objects.

Using scripts in PackageMaker (Mac OS X)

I'm trying to build an install package on Mac for my application(s) (there are two .app-s). One of them is a GUI and the other is an agent (service). The install package must run a script (that's the only way I know it can be done) at the end of the installation, so that the agent would be installed as an agent. There is also a .plist file that is installed in /Library/LaunchAgents, but that's what PackageMaker does automatically (ie without the need of a script).
The problem is that... I can't make the script run. I get "Installation failed" at the end.
I don't know what the problem is. Can somebody please help?
A few more details:
I'm trying to do this at the postflight step (after the installation is done). just a guess... do I need to call my file with the same name (eg. postflight)?
I suppose this is the correct directory and path I have set. After dozens of attempts, in the last one I set as "relative path" the path where the PackageMaker document is (in the same folder I have the script). Is there something important I should know about paths that might cause the problem? (the fact is, however, that the build is successful)
do I have to write something specific to allow the script to be executed? I've got the following script:
MyInstall.sh
with the following contents (without quotes):
"#!/bin/sh
sudo launchctl load /Library/LaunchAgents/com.MyCompany.MyService.plist"
(note that com.Mycompany.MyService.plist is already there - it puts it there on install).
P.S. In some of my attempts to make it work, PackageMaker crushed.
Can anybody PLEASE help?
You can look at the installation log.
When the Installer application is running, go to the Window menu and select Installation Log. This will open a separate log window. In this window, select Display All from the combo box. You will be able to see why your script is failing.
I had such experience myself. And here's what i've found out.
In order for installation to succeed your sript must explicitly return 0(i.e. 'success').
If result of commands in your script is not 0 and you aren't calling
exit 0
as last script sentense, your install will tell you it failed.
If you don't use script's return value and you are sure scrpit works correct - just add 'exit 0' at the last line of script.

Resources