Skip to content

Instantly share code, notes, and snippets.

@Kvnbbg
Forked from HuangJiaLian/make_dmg.sh
Last active April 1, 2024 15:35
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Kvnbbg/84871ae4d642c2dd896e0423471b1b52 to your computer and use it in GitHub Desktop.
Save Kvnbbg/84871ae4d642c2dd896e0423471b1b52 to your computer and use it in GitHub Desktop.
Two steps to turn a Python file to a macOS installer
#!/bin/sh
# References
# https://www.pythonguis.com/tutorials/packaging-pyqt5-applications-pyinstaller-macos-dmg/
# https://medium.com/@jackhuang.wz/in-just-two-steps-you-can-turn-a-python-script-into-a-macos-application-installer-6e21bce2ee71
# ---------------------------------------
# Clean up previous builds
# ---------------------------------------
echo "Cleaning up previous builds..."
rm -rf build dist/*
# ---------------------------------------
# Step 1: Convert Python script to an application bundle
# ---------------------------------------
echo "Converting Python script to macOS app bundle..."
# The following command will create a standalone .app from your Python script
pyinstaller --name 'CryptoSafePDF' \
--icon 'CryptoSafePDF.ico' \
--windowed \
--add-data='./strong_beat.wav:.' \
--add-data='./sub_strong_beat.wav:.' \
--add-data='./weak_beat.wav:.' \
main.py
# ---------------------------------------
# Step 2: Convert the application bundle to a DMG (macOS disk image)
# ---------------------------------------
echo "Creating DMG installer..."
# Prepare the folder for DMG creation
mkdir -p dist/dmg
rm -rf dist/dmg/*
cp -r "dist/CryptoSafePDF.app" dist/dmg
# Create the DMG
# Ensure you have 'create-dmg' installed. If not, install using 'brew install create-dmg'
create-dmg \
--volname "CryptoSafePDF" \
--volicon "CryptoSafePDF.ico" \
--window-pos 200 120 \
--window-size 600 300 \
--icon-size 100 \
--icon "CryptoSafePDF.app" 175 120 \
--hide-extension "CryptoSafePDF.app" \
--app-drop-link 425 120 \
"dist/CryptoSafePDF.dmg" \
"dist/dmg/"
echo "Packaging complete. You can find the DMG installer in the dist/ directory."
#!/bin/bash
# CryptoSafePDF Setup and Packaging Script for macOS
# 1. Install Homebrew (if not installed)
if ! command -v brew &>/dev/null; then
echo "Installing Homebrew..."
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
else
echo "Homebrew already installed."
fi
# 2. Install create-dmg
if ! brew list create-dmg &>/dev/null; then
echo "Installing create-dmg..."
brew install create-dmg
else
echo "create-dmg already installed."
fi
# 3. Install pyinstaller
if ! pip list | grep pyinstaller &>/dev/null; then
echo "Installing pyinstaller..."
pip install pyinstaller
else
echo "pyinstaller already installed."
fi
# 4. Clean up previous builds
echo "Cleaning up previous builds..."
rm -rf build dist/*
# 5. Convert Python script to an application bundle
echo "Converting Python script to macOS app bundle..."
pyinstaller --name 'CryptoSafePDF' \
--icon 'CryptoSafePDF.ico' \
--windowed \
--add-data='./strong_beat.wav:.' \
--add-data='./sub_strong_beat.wav:.' \
--add-data='./weak_beat.wav:.' \
main.py
# 6. Create the DMG installer
echo "Creating DMG installer..."
mkdir -p dist/dmg
rm -rf dist/dmg/*
cp -r "dist/CryptoSafePDF.app" dist/dmg
create-dmg \
--volname "CryptoSafePDF" \
--volicon "CryptoSafePDF.ico" \
--window-pos 200 120 \
--window-size 600 300 \
--icon-size 100 \
--icon "CryptoSafePDF.app" 175 120 \
--hide-extension "CryptoSafePDF.app" \
--app-drop-link 425 120 \
"dist/CryptoSafePDF.dmg" \
"dist/dmg/"
echo "Packaging complete. You can find the DMG installer in the dist/ directory."
@Kvnbbg
Copy link
Author

Kvnbbg commented Aug 30, 2023

  1. Install Homebrew (if you haven't already):
    Homebrew is a package manager for macOS. It helps you install software and manage dependencies.

    Open the terminal and run:

    /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
  2. Install create-dmg:
    create-dmg is a shell script used in the provided script to create .dmg files.

    Once you have Homebrew installed, you can install create-dmg with:

    brew install create-dmg
  3. Install pyinstaller:
    You can install pyinstaller using pip. If you don't have pip, you should install it first.

    pip install pyinstaller
  4. Script Permissions:
    Make sure the script has execute permissions. Navigate to the directory containing the script and run:

    chmod +x your_script_name.sh
  5. Run the Script:
    Finally, you can run the script with:

    ./your_script_name.sh
  6. Additional Note:
    Ensure you have the necessary files (CryptoSafePDF.ico, strong_beat.wav, sub_strong_beat.wav, weak_beat.wav, and main.py) in the same directory as the script or adjust the paths in the script to point to the correct locations.

@Kvnbbg
Copy link
Author

Kvnbbg commented Aug 30, 2023

If you're receiving a "command not found" error for both pyinstaller and pip, it may indicate a few potential issues:

  1. Python/Pip Not Installed: Python or Pip might not be installed on your system.
  2. PATH Issue: The location of pip and pyinstaller might not be in your system's PATH. This means that the system doesn't know where to look for these executables.
  3. Multiple Python Versions: If you have multiple versions of Python installed, the pip command might be named differently, e.g., pip3.

Let's address these potential issues step-by-step:

1. Check if Python is installed:

Run the following command:

python --version

or if you primarily use Python 3:

python3 --version

If Python is installed, this command should return the version of Python you have. If not, you need to install Python.

2. Check if Pip is installed:

Depending on your Python version, try:

pip --version

or

pip3 --version

If neither works, you need to install pip.

3. Install Pip (if not installed):

If you have Python, but not pip, you can install pip with:

curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
python get-pip.py

or for Python 3:

python3 get-pip.py

4. Install PyInstaller (if Pip is working):

Once you have pip or pip3 working, install PyInstaller:

pip install pyinstaller

or

pip3 install pyinstaller

5. Adjust the PATH (if needed):

If you're sure pip and pyinstaller are installed, but you still get "command not found", it's likely a PATH issue.

To see if pip and pyinstaller are in your PATH, try:

echo $PATH

Check the output to see if the path to pip and pyinstaller is there.

If not, you may need to add them. The exact path depends on how and where Python and pip are installed.

Typically, for a user-based installation, the executable path might be something like /Users/your_username/Library/Python/x.x/bin. If this is the case, you can add it to your PATH by adding the following to your .zshrc:

export PATH="/Users/your_username/Library/Python/x.x/bin:$PATH"

Replace your_username with your actual username and x.x with your Python version.

After editing .zshrc, refresh your shell or restart the terminal.

6. Re-Check:

After these steps, try running pip and pyinstaller again to see if they're recognized.

@Kvnbbg
Copy link
Author

Kvnbbg commented Aug 30, 2023

Certainly! If pyinstaller is causing issues, there are other tools and methods to package Python applications:

  1. cx_Freeze:
    It's a set of scripts and modules for freezing Python scripts into executables in a manner similar to pyinstaller, py2exe, etc.

    Install with:

    pip install cx_Freeze

    Basic usage:

    cxfreeze your_script.py --target-dir dist
  2. py2app (for macOS specifically):
    It's a Python setuptools command which will allow you to make standalone macOS application bundles.

    Install with:

    pip install py2app

    Basic usage involves creating a setup.py script:

    from setuptools import setup
    
    APP = ['your_script.py']
    DATA_FILES = []
    OPTIONS = {
        'argv_emulation': True,
        'packages': ['required_package1', 'required_package2'],
    }
    
    setup(
        app=APP,
        data_files=DATA_FILES,
        options={'py2app': OPTIONS},
        setup_requires=['py2app'],
    )

    Then, run:

    python setup.py py2app
  3. Briefcase:
    This is a tool by the BeeWare project that packages Python projects as native apps. It has the advantage of supporting multiple platforms.

    Install with:

    pip install briefcase

    Usage involves more steps as it's intended to be used with a full BeeWare application, but the BeeWare tutorial can guide you through it.

  4. Docker:
    While not a native packaging tool, Docker can help you create a containerized version of your application, ensuring it runs consistently across different environments. This would require the end-user to have Docker installed, though.

Remember: Each tool might have its peculiarities, and the packaging process might need adjustments depending on the complexity of your application, especially regarding dependencies, data files, and binary extensions.

@Kvnbbg
Copy link
Author

Kvnbbg commented Aug 31, 2023

To call and run installer.py from main.py, you can use one of the following methods:

  1. Using the exec Function:
    This method allows you to execute a string as Python code. It's a straightforward way to run another Python script from within a script.

    # main.py
    with open('installer.py', 'r') as file:
        exec(file.read())
  2. Using the subprocess Module:
    This method runs the script as a separate process. It's more versatile and allows for better control over the execution, capturing output, and error handling.

    # main.py
    import subprocess
    
    subprocess.run(["python", "installer.py"])

    Note: Replace python with python3 if that's the command you use to run Python scripts on your system.

  3. Importing as a Module:
    If installer.py is structured in a way that allows it to be imported (i.e., it doesn't execute code when imported), you can simply import it and call its functions.

    # main.py
    import installer
    
    # Call functions from installer.py if needed

    For this to work without modifications, you'd typically structure installer.py with a main guard:

    # installer.py
    def main():
        # All the main code of installer.py
    
    if __name__ == "__main__":
        main()

    Then, in main.py, you can call the main() function of installer.py:

    # main.py
    import installer
    
    installer.main()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment