Setting up a Software Development Environment for Mac OS, Ubuntu Linux and Windows

Sean E. O'Connor

INTRODUCTION

I show how to set up a software development environment for Mac OS Ubuntu Linux and Windows When we're done, we'll be able to develop programs in C, C++, Python, Javascript, and Lisp using Unix tools.

SYSTEM SETUP - Mac OS - Ubuntu Linux - Windows

Ubuntu Linux

Ubuntu Desktop.
Ubuntu Desktop

I will install on an early era MacBook Pro: MacBook Pro (17-inch, Early 2009) running OS X El Capitan.

You can read through this excellent guide, which shows how to install a boot loader so you can dual-boot Mac OS and Ubuntu Linux, and how to convert and load a Ubuntu Linux distribution onto a USB drive so you can install Ubuntu Linux. How to install Linux on a Mac (the definitive guide)

Now you've got your USB drive containing the Ubuntu Linux distribution plugged into your Mac and your Ethernet cable hooked up to your internet provider. Boot up and hold down the Option key. Select the EFI Boot icon in the boot device menu.

Ubuntu Boot from USB. Ubuntu Select USB Boot Drive
Booting from the USB Boot Drive

Now go through the Ubuntu installation process. Pretty straightforward for the most part. You'll want to select both Download updates while installing Ubuntu and Install third-party software for graphics,...

Ubuntu Formatting.
Download Updates and Third Party Software.

You'll see This computer currently has no detected operating systems. Select Something else. Now you'll format the main disk drive (be careful that you don't overwrite your Mac OS drive, and create swap space. Format the Ubuntu Linux boot drive as Ext4 journaling file system with mount point /, then create a swap drive.

Ubuntu Formatting. Mac and Ubuntu Linux Partitions
Mac and Ubuntu Linux Partitions

On the other hand, if you are reinstalling Ubuntu Linux, it's slightly different,

Ubuntu Erase Reinstall. Mac and Ubuntu Linux Partitions
Booting from the USB Boot Drive

Enter user name and password. You will be the superuser.

Ubuntu Boot from USB.
User Name and Password

Finish up the Ubuntu installation by updating and upgrading all software. The upgrade will take some time.

sudo apt-get update sudo apt-get upgrade

You'll need to explicitly install the Wireless driver. I found it by looking at Broadcom Linux Wireless Drivers Install using the line below and reboot after you install.

sudo apt install bcmwl-kernel-source

Fixing cursor flicker. Go to System Settings > Displays. You will see two displays: a red and a green. Turn off the green display.

Ubuntu Display Settings Part 1. Ubuntu Display Settings Part 2.
Display Settings

To read a USB drive formatted in Extended FAT format, install the drivers.

sudo apt-get install exfat-utils exfat-fuse

Install Vim first so you can edit files. To install file sharing across computers, find out your username

users seanoconnor

then install Samba

sudo apt-get install samba sudo smbpasswd -a seanoconnor mkdir /home/seanoconnor/share sudo cp /etc/samba/smb.conf ~

Edit the smb.conf file by adding these lines to the end,

[share] path = /home/seanoconnor/share valid users = seanoconnor read only = no
sudo apt-get install smbclient sudo service smbd restart

To speed up SMB sharing, (it still takes a minute or two on my Mac), add the IP addresses and hostnames of all your local computers to the /etc/hosts file

hostname seanoconnor-MacBookPro

The last two entries are my Ubuntu Linux computer and my Mac OS computer,

127.0.0.1 localhost ... 333.333.3.33 seanoconnor-MacBookPro 333.333.3.34 Artificer

Go into the file manager, into Home directory, and in Properties, turn on sharing in Local Network Share and allow others to create and delete files. Now you can see your shared drive on another computer under the share name seanoconnor-mac, directory share with login password whatever you set for samba

Back on Mac OS, mount the shared remote directory, which will prompt you for the password,

cd ~/Desktop mkdir Ubuntu mount -t smbfs //seanoconnor@seanoconnor-MacBookPro/Desktop ~/Desktop/Ubuntu

Here's a script for rsyncing some of my Mac directories to my Ubuntu Linux machine:

#!/bin/sh # # syncToLinux # # Sync selected directories from my Mac OS machine to my Linux machine. # Mount the remote Linux drive on my Mac. ipAddress="333.333.333.333" userName="seanoconnor" localShareName="${HOME}/Desktop/Ubuntu" sourceDir="${HOME}/Desktop/Sean" # Mac doesn't have the source directory! Shouldn't happen. if [ ! -d ${sourceDir} ] ; then echo "Source drive ${sourceDir} isn't mounted on the Mac...Skipping the backup!" exit 1 fi # Create the remote directory, then mount the Linux directory. if [ ! -d ${localShareName} ] ; then echo "Mounting the Linux remote directory..." mkdir ${localShareName} mount_smbfs "//${userName}@${ipAddress}/Desktop" "${localShareName}" status=$? if [ ${status} != 0 ] ; then echo "ERROR: Failed mounting remote directory //${userName}@${ipAddress}/Desktop onto local directory ${localShareName}...Skipping the backup!" rmdir ${localShareName} exit 1 fi fi # rsync directories from my Mac to my Ubuntu Linux machine. rsyncSettings="-avz --progress --delete" # --dry-run" sourceDir="${HOME}/Desktop/Sean" destDir="${HOME}/Desktop/Ubuntu/Sean" if [ ! -d ${destDir} ] ; then echo "Linux drive ${sourceDir} isn't mounted on the Linux machine...Skipping the backup!" exit 1 fi echo "Syncing my artwork" rsync ${rsyncSettings} ${sourceDir}/Arts/Visual/Painting/OriginalWorks/ ${destDir}/OriginalWorks echo "Syncing my web page (but not MathJax)" rsync ${rsyncSettings} --exclude 'MathJax' ${sourceDir}/WebSite/ ${destDir}/WebSite # Unmount and remove the remote directory. hdiutil detach ${localShareName} rmdir ${localShareName}

To add a sort of Multitouch, install the touchegg app which runs as a daemon in user space.

sudo apt install touchegg

Edit or create a file ~/.xprofile in your home directory which reconfigures some keys and runs touchegg as a background process upon startup.

seanoconnor@seanoconnor-MacBookPro:~$ cat .xprofile synclient ClickFinger3=0 synclient TapButton3=0 touchegg &
Configure the gestures in the file ~/.config/touchegg/touchegg.conf
<touchegg> ... <application name="All"> ... # Three finger drag moves windows. <gesture type="DRAG" fingers="3" direction="ALL"> <action type="MOVE_WINDOW">action> <gesture> ...

Then reboot so that touchegg gets loaded upon startup.

For debugging you can kill the background touchegg process, then run touchegg from the terminal window, and see commands being executed.

Mac OS runs .bash_profile first then .bashrc because it makes all terminal windows login windows. Not so for Ubuntu. So open Terminal->Preferences->Profiles->Edit->Command and turn on Run command as login shell.

Some other utilites you'll want to install,

  1. Thunderbird (email)
  2. Screenshot (screen capture)
  3. Quadrapassel (like Tetris)
  4. Cheese Webcam Booth (photo app)

Mac OS

You get everything installed out of the box. You do have to configure your System Preferences.

Windows

You get everything installed out of the box.

PROGRAMMING TOOLS

Developer Tools Mac

Mac OS X comes with most of the the Unix utilities make, diff, gcc, g++ (the clang equivalent), lldb, etc. already installed. To get recent versions, you can install the free XCode IDE from the Apple App Store

Download XCode from Apple App Store.
Download XCode from Apple App Store

Developer Tools Ubuntu Linux

Naturally, you've got all the unix tools or can easily install them using sudo apt-get from the terminal.

UNIX Tools Windows

Load Cygwin utilities from Cygwin home. Choose download to disk without installing to C:/Install/Cygwin Use direct connection and pick a mirror site. Next, run setup.exe. Pick view=full. Now select all the defaults and also be sure to add bash, bison, byacc, cvs, ctags, diff, flex, gcc, g++, gdb, hexedit, ncurses, make, python, sed, tar, zip and unzip. Then run setup again but this time install from the local disk. You'll get a cygwin icon on the desktop which you can launch shell commands from.

Cygwin Setup Part 1. Cygwin Setup Part 2. Cygwin Setup Part 3.
Cygwin Setup

Bash Settings

I'm using the BASH shell. Read the Beginner's Guide, Tutorial, and Reference then go to your home directory, and place the bash startup files .bash_login, .bash_logout and .bashrc there.

In Windows the home directory is in C:/Documents and Settings/Sean Erik O'Connor. Note: If your user name in your /home directory contains spaces, it will cause trouble. cygwin gets it from the windows logon name. You can edit the /etc/passwd file and change the Cygwin user name (first field), then rename your directory.

#============================================================================= # # FILE NAME # # .bash_profile # # DESCRIPTION # # bash shell startup file for Unix systems, executed upon login. # # Install into your home directory ~. Also called .profile or # .bash_login # # DATE # # 23 Feb 17 # # AUTHOR # # Sean E. O'Connor # #============================================================================= # #------------- Portability ------- # # Try to determine which system we are running on. #----------------------------------------------------------------------------- #--- Set the path #----------------------------------------------------------------------------- # Be sure to put useful scripts and executables into the home bin directory: ~/bin bins="/usr/local/bin:~/bin" # To set the hostname manually on Mac OS X # sudo hostname Artificer" # sudo scutil --set LocalHostName Artificer # sudo scutil --set ComputerName Artificer # sudo scutil --set HostName Artificer # In System Preferences/Sharing change 'Computer Name' to Artificer. # In your local network configuration, rename the name provided by DHCP. # Find out the hostname. In cygwin bash, strip off the trailing \r introduced. # The alternative is to use tr -d '\r' hostname=`python -c "import platform; print( platform.node() )" | sed 's/^[ \r\n\t]*$//'` if [ "${hostname}" == "" ] ; then echo "Could not get a hostname. We will guess a new Mac system!" hostname="Artificer" fi # Installing Python 3 on the Mac: # ------------------------------- # Go to # http://www.python.org/ # Create a symbolic link to the new python version, # cd /Library/Frameworks/Python.framework/Versions/3.x/bin # sudo ln -s python3.x python # Verify we are calling the new python using # python -V # The python installer will automatically prefix the directory # :/Library/Frameworks/Python.framework/Versions/3.x/bin: # to your path at the end of this file. Remove it. # # To call the default python2.7.2 for Mac OS X in a terminal window, comment out this line, and do # source ~/.bash_profile # # Installing Python 3 on Ubuntu Linux # ----------------------------------- # Just put a symbolic link to python 3 in /usr/local/bin as follows, # cd /usr/local/bin" # sudo ln -s /usr/bin/python3.6 python" # Test using # echo " python -V" # Mac OS if [ "${hostname}" == "Artificer" ] ; then py3bin="/Library/Frameworks/Python.framework/Versions/3.6/bin" ###echo "Using Mac OS X System Python version path = ${PATH}" # Ubuntu Linux elif [ "${hostname}" == "seanoconnor-MacBookPro" ] ; then py3bin="/usr/local/bin" ###echo "Using Ubuntu Linux Python version path = ${PATH}" fi # CMake tool used for Blender. cmakebin="/Applications/CMake.app/Contents/bin/" PATH="${HOME}:${cmakebin}:${py3bin}:${bins}:${PATH}" export PATH # Error check: # Make sure Python 3 is installed on your system. Get the version and redirect from stderr to stdout. python_version=`python -V 2>&1` # Delete minor versions (numbers after the first dot), and spaces, e.g. Python 3.6.0 => Python3 python_version_stripped=`echo ${python_version} | sed 's/\.[0-9]//g' | sed 's/[ \r]//g'` # Check the version and give help. if [ "${python_version_stripped}" != "Python3" ] ; then echo "WARNING: Calling o l d python version ${python_version} from `which python` in path $PATH" fi # Default settings. ls_color_option="-G" desk_dir="${HOME}/Desktop" thumb_dir="/Volumes/ALNILAM" extra_bin_path="/usr/local/bin" # Windows 7 64-bit running in Parallels running in Mac OS. if [ "${hostname}" == "SEANOCONNORBC36" ] ; then ### echo "Overriding Mac settings --- this is a Windows machine!" ls_color_option="--color=tty" desk_dir="/cygdrive/c/cygwin/home/Sean" thumb_dir="/cygdrive/e" # Windows has endless trouble with blanks in filenames, so use DOS 8.3 equivalent file name. desk_dir="/cygdrive/c/docume~1/Sean/Desktop" fi #------------- Export base directories for use by other programs ------------- # The root directory has /Sean under it and /Sean/WebSite underneath that. export desk_dir export thumb_dir #echo "Hostname = |${hostname}|" #echo "Root dir |${desk_dir}|, thumb dir |${thumb_dir}|, desk_dir |${desk_dir}|" #------------- Directory Shorthands ------------- # Top level directories sean_dir="${desk_dir}/Sean" app_dir="${desk_dir}/Apps" export sean_dir export app_dir # Level 1. arts_dir="${sean_dir}/Arts" business_dir="${sean_dir}/Business" family_dir="${sean_dir}/Family" science_dir="${sean_dir}/Sciences" web_dir="${sean_dir}/WebSite" export arts_dir export business_dir export family_dir export science_dir export web_dir # Quickly cd to subdirectories by typing cd subdir. # Need . in the list to avoid having to put ./ in front of directories. export CDPATH=.:~:${sean_dir}:${pp_src_dir} # Mac system tweaks. if [ "${hostname}" == "Artificer" ] ; then # Show hidden files in finder (needs a relaunch of finder). defaults write com.apple.finder AppleShowAllFiles TRUE fi # Finish up the aliases. source .bashrc export PATH
#---------------------------------------------------------------------------- # # TITLE # # .bashrc # # DESCRIPTION # # # Bourne Again Shell (bash) startup file for Unix systems. Executed # everytime we start a subshell. Install into your home directory ~. # Put aliases and functions here. # # Use source .bashrc to reset the environment after you are in a # terminal window. Place the line "source .bashrc" into .bash_profile # to execute this file's commands upon login. # # To debug, use sh -x .bashrc # # DATE # # 27 Jul 11 # # AUTHOR # # Sean E. O'Connor # #---------------------------------------------------------------------------- #------------- Aliases ------------- # # Be sure to put useful scripts and executables into the home bin directory, ~/bin or global /usr/local/bin # alias desk='cd ${desk_dir}' alias thum='cd ${thumb_dir}' alias sean='cd ${sean_dir}' alias app='cd ${app_dir}' alias art='cd ${arts_dir}/Visual/Painting/OriginalWorks' alias bus='cd ${business_dir}' alias fam='cd ${family_dir}' alias sci='cd ${science_dir}' alias web='cd ${web_dir}' alias math='cd ${science_dir}/Mathematics' alias comp='cd ${science_dir}/ComputerScience' alias acc='cd ${business_dir}/Accounts' web_des_dir="${web_dir}/WebDesign" alias wd='cd "${web_dir}/WebDesign"' web_main_dir="${web_des_dir}/MaintainWebPage/" alias main='cd ${web_dir}/WebDesign/MaintainWebPage' alias artw='cd ${web_dir}/Art' crc_dir="${web_dir}/CommunicationTheory/ChannelCoding/Crc" alias lif='cd ${web_dir}/ComputerScience/Automata/Life' alias lis='cd ${web_dir}/ComputerScience/Compiler/ParserGeneratorAndParser/SourceCode/ParserGenerator' pp_dir="${web_dir}/Mathematics/AbstractAlgebra/PrimitivePolynomials" pp_proj_dir="${pp_dir}/Project" pp_bld_dir="${pp_dir}/Project/Build" pp_src_dir="${pp_dir}/Project/SourceCode" pp_exe_dir="${pp_bld_dir}/Bin" alias pp='cd ${pp_dir}' alias ppb='cd ${pp_bld_dir}' alias pps='cd ${pp_src_dir}/Primpoly' alias ppsc='cd ${pp_src_dir}/PrimpolyC' # Other directories. #------------- Git ------- # Location of git repository. export GITREPOS="${web_dir}/private/repos" #------------- Set prompt ------- # # Define colors for the text in a prompt. # startcolor="\[\e[" black="30" red="31" green="32" yellow="33" blue="34" magenta="35" teal="36" white="37" separator=";" blackbackground="40" redbackground="41" greenbackground="42" yellowbackground="43" bluebackground="44" magentabackground="45" tealbackground="46" whitebackground="47" reset="0" boldtext="1" underline="4" blink="5" inverted="7" endcolor="m\]" resetcolor="\e[0m" whiteonblue="${startcolor}${white}${separator}${bluebackground}${endcolor}" redonblue="${startcolor}${red}${separator}${bluebackground}${endcolor}" # Set the prompt to # time \@, date \d, user name \u, host name \h, current directory \w # \W basename of current directory, \$ if UID = 0 (root), use # instead of $ export PS1="${redonblue}\u:${whiteonblue}\w${resetcolor}\$ " ###echo ${PS1} #------------- Shell options ------------- # # Set vi edit mode for the command line. # Hit <ESC> to go into vi's edit command mode: # h Move cursor left # l Move cursor right # A Move cursor to end of line and put in insert mode # 0 (zero) Move cursor to beginning of line (doesn't put in insert mode) # i Put into insert mode at current position # a Put into insert mode after current position # dd Delete line (saved for pasting) # D Delete text after current cursor position (saved for pasting) # p Paste text that was deleted # j Move up through history commands # k Move down through history commands # u Undo set -o vi # Don't wait for job termination notification set -o notify # Don't use ^D to exit set -o ignoreeof # Use case-insensitive filename globbing shopt -s nocaseglob # Make bash append rather than overwrite the history on disk shopt -s histappend # When changing directory small typos can be ignored by bash # for example, cd /vr/lgo/apaache would find /var/log/apache shopt -s cdspell shopt -s cdable_vars #------------- Completion options ------------- # # These completion tuning parameters change the # default behavior of bash_completion: # Define to avoid stripping description in --option=description of './configure --help' COMP_CONFIGURE_HINTS=1 # Define to avoid flattening internal contents of tar files COMP_TAR_INTERNAL_PATHS=1 # If this shell is interactive, turn on programmable completion enhancements. # Any completions you add in ~/.bash_completion are sourced last. case $- in *i*) [[ -f /etc/bash_completion ]] && . /etc/bash_completion ;; esac #------------- History options ------------- # # Don't put duplicate lines in the history. export HISTCONTROL="ignoredups" # Ignore some controlling instructions export HISTIGNORE="ls:ls *:[ ]*:&:cd:cd ..:exit:hi:s:f:m:um" # Whenever displaying the prompt, write the previous line to disk export PROMPT_COMMAND="history -a" #------------- Aliases ------------- # # If these are enabled they will be used instead of any instructions # they may mask. For example, alias rm='rm -i' will mask the rm # application. # # To override the alias instruction use a \ before, ie # \rm will call the real rm not the alias. # # To see all aliases, type alias. # Use unalias to remove a definition. # Interactive operation... alias rm='rm -i' alias cp='cp -i' alias mv='mv -i' alias up='cd ..' # Default to human readable figures alias df='df -h' alias du='du -hac' # Misc :) alias less='less -r' # raw control characters alias whence='type -a' # where, of a sort alias grep='grep --color' # show differences in colour alias hi=history # Some shortcuts for different directory listings alias ls='ls -hF ${ls_color_option}' alias dir='ls --color=auto --format=vertical' alias ll='ls -l' # long list alias la='ls -A' # all but . and .. alias l='ls -CF' # #------------- Utility functions ------------- # Recursive search for a string in a file. function grepall() { if [ $# == 0 ] then echo "Usage: grepall <string>" fi # Grab the function argument, bash style. pat=$1 echo "Searching all subdirectories for pattern ${pat}" find . -name '*.[ch]' -exec grep -iH "${pat}" {} ';' find . -name '*.hpp' -exec grep -iH "${pat}" {} ';' find . -name '*.cpp' -exec grep -iH "${pat}" {} ';' find . -name '*.py' -exec grep -iH "${pat}" {} ';' find . -name '*.m' -exec grep -iH "${pat}" {} ';' find . -name '*.js' -exec grep -iH "${pat}" {} ';' find . -name '*.java' -exec grep -iH "${pat}" {} ';' find . -name '*.pl' -exec grep -iH "${pat}" {} ';' find . -name '*.prl' -exec grep -iH "${pat}" {} ';' find . -name '*.html' -exec grep -iH "${pat}" {} ';' find . -name '*.css' -exec grep -iH "${pat}" {} ';' find . -name 'makefile' -exec grep -iH "${pat}" {} ';' find . -name '*.dat' -exec grep -iH "${pat}" {} ';' find . -name '*.txt' -exec grep -iH "${pat}" {} ';' } function touchall() { find . -exec touch {} ';' } function testOptions() { if [ $# == 0 ] then echo "Number of arguments to testOptions is $#" fi # No spaces around the equals allowed in bash! a1=$1 echo "You said |${a1}|" # Compare the first 3 letters. if [ "${a1:0:3}" == "tes" ] then echo "You said testOptions tes" else echo "What did you say?" fi } # Launch gvim editor. function gvim() { if [ $# == 0 ] then fileName="${HOME}/temp.txt" echo -n > ${fileName} echo "Opening temporary file ${fileName}" open -a MacVim "${fileName}" else fileName=$1 fi open -a MacVim "${fileName}" } # Remove temporary files. function cleanall() { if [ $# != 0 ] then echo "Usage: cleanall" fi find . -name '*~' -print -exec rm -f {} \; find . -name '._*' -print -exec rm -f {} \; find . -name '.DS_Store*' -print -exec rm -f {} \; find . -name 'Thumbs.db' -print -exec rm -f {} \; find . -name '*.swp' -print -exec rm -f {} \; find . -name '*.o' -print -exec rm -f {} \; find . -name '*.class' -print -exec rm -f {} \; find . -name '*.o~$' -print -exec rm -f {} \; find . -name '*.o~>' -print -exec rm -f {} \; find . -name '*.dSYM' -print -exec rm -rf {} \; find . -name '*.obj' -print -exec rm -rf {} \; find . -name '*.ncb' -print -exec rm -rf {} \; find . -name '*.suo' -print -exec rm -rf {} \; find . -name '*.idb' -print -exec rm -rf {} \; find . -name '*.pdb' -print -exec rm -rf {} \; find . -name '*.manifest' -print -exec rm -rf {} \; find . -name '*.Spotlight-V100' -print -exec rm -rf {} \; find . -name '*.Trash*' -print -exec rm -rf {} \; find . -name '*.fseventsd' -print -exec rm -rf {} \; }
#============================================================================= # # FILE NAME # # .bash_logout # # DESCRIPTION # # bash shell executed upon logout. # Install into your home directory ~. # # DATE # # 19 Apr 10 # # AUTHOR # # Sean E. O'Connor # #============================================================================= # # Clean up. # rm -f ~/.bash_history #rm -f ~/.viminfo

Git

I use Git for source code control. It's out of the box in Mac OS and you can install in Ubuntu Linux using

sudo apt install git

It's described in the Git Pro 2 Book. Here's an example of how I set up my Git repository for my Primpoly project using local repositories.

Git Configuration.

In your .bashrc file in home directory, add the location of the local Git repository,

#------------- Git ------- # Location of git repository.

The bash parameter ${web_dir} points to my top level web directory on my disk.

In your home directory $HOME, edit the file .gitconfig to add your email and user name plus your default editor,

[user] email = seanerikoconnor@gmail.com name = Sean E. O'Connor [core] editor = vim excludesfile = /Users/seanoconnor/.gitignore [merge] tool = opendiff conflictstyle = diff3 [difftool] prompt = false # Don't prompt the user to hit RETURN before the next file difference. [difftool "opendiff"] # cmd = echo 'base(ancestor base for the merge) = ' \"$BASE\" 'local(file on current branch) = ' \"$LOCAL\" 'remote(file to be merged) = ' \"$REMOTE\" 'merged(what mergetool should write) = ' \"$MERGED\" cmd = opendiff \"$LOCAL\" \"$REMOTE\" -merge \"$MERGED\" [mergetool "opendiff"] cmd = opendiff \"$LOCAL\" \"$REMOTE\" -ancestor \"$BASE\" -merge \"$MERGED\" trustExitCode = true [push] default = simple [credential] helper = cache [alias] unstage = reset HEAD -- uncheckin = checkout --
and which files to ignore globally by git,
$ cd $ cat .gitignore *.html
Create a Bare Repository

Create a local directory to serve as the Git repository.

$ mkdir repos ; cd repos
Make a subdirectory for the project, then initialize the repository.
$ mkdir Primpoly ; cd Primpoly $ git --bare init Initialized empty Git repository in $web_dir/private/repos/Primpoly/ $ la HEAD branches/ config description hooks/ info/ objects/ refs/
Add Files to Source Code Control

Go to the directory containing the source files and initialize Git.

cd ~/Desktop/Sean/WebSite/Mathematics/AbstractAlgebra/PrimitivePolynomials/Project/SourceCode/Primpoly $ git init Initialized empty Git repository in /Users/seanoconnor/Desktop/Sean/WebSite/Mathematics/AbstractAlgebra/PrimitivePolynomials/Project/SourceCode/Primpoly/.git/

Place the source files under source code control. We don't add the html files since they are automatically generated.

$ git add *.cpp *.h /FactorTables $ git status On branch master Initial commit Changes to be committed: (use "git rm --cached <file>..." to unstage) new file: FactorTables/c02minus.txt ... new file: FactorTables/c12plus.txt new file: Primpoly.cpp ... new file: ppUnitTest.h Untracked files: (use "git add <file>..." to include in what will be committed) Primpoly.cpp.html ... ppUnitTest.h.html

Your files are staged now. When ready, do a commit,

$ git commit -m "Initial import from SVN." [master (root-commit) 75fa9b1] Initial import from SVN. 32 files changed, 46548 insertions(+) create mode 100755 FactorTables/c02minus.txt ... create mode 100755 FactorTables/c12plus.txt create mode 100755 Primpoly.cpp ... create mode 100755 ppUnitTest.h

Now we let the local directory Primpoly know about the remote repository.

$ git remote add origin $GITREPOS/Primpoly/ $ git remote show origin $ git remote -v origin ${web_dir}/private/repos/Primpoly/ (push)

Then we push up to the remote repository,

$ git push origin master Counting objects: 35, done. Delta compression using up to 8 threads. Compressing objects: 100% (35/35), done. Writing objects: 100% (35/35), 1.10 MiB | 0 bytes/s, done. Total 35 (delta 2), reused 0 (delta 0) To $GITREPOS/Primpoly/ * [new branch] master -> master

To avoid specifying origin and master each time you pull and push, you can track the remote repository,

$ git pull There is no tracking information for the current branch. Please specify which branch you want to merge with. See git-pull(1) for details. git pull <remote> <branch> If you wish to set tracking information for this branch you can do so with: git branch --set-upstream-to=origin/<branch> master $ git branch --set-upstream-to=origin/master Branch master set up to track remote branch master from origin.

Verify the git repository is set up correctly by doing a test clone from the remote repository, and verify there are no differences between it and your local repository,

$ cd ~/Desktop $ git clone $GITREPOS/Primpoly Cloning into 'Primpoly'... done. $ diff -r ~/Desktop/Primpoly \ ~/Desktop/Sean/WebSite/Mathematics/AbstractAlgebra/PrimitivePolynomials/Project/SourceCode/Primpoly

You can use Mac OS X mergetool/opendiff to resolve conflicts.

$ git mergetool

Make

Somewhat long in the tooth, I use make

PROGRAMMING LANGUAGES

C++ Compilers

Start by reading the textbooks A Tour of C++ by Bjarne Stroustrup and The C++ Programming Language, 4th Edition then read the latest updates in the C++ Super-FAQ and the C FAQ

Mac OS X development tools contain the clang C++ and C compilers. They come with XCode.

Ubuntu Linux To get C++ and C working in Ubuntu Linux, install clang and explicity install one of its libraries,

sudo apt-get install clang sudo apt-get install libc++-dev

Compiles will fail, as shown here,

$ cat foo.cpp #include <string> #include <iostream> using namespace std; int main(int argc,char** argv) { string s(argv[0]); cout <<s <<endl; } $ clang++ -std=c++11 -stdlib=libc++ foo.cpp In file included from foo.cpp:1: In file included from /usr/include/c++/v1/iostream:38: In file included from /usr/include/c++/v1/ios:216: In file included from /usr/include/c++/v1/__locale:15: /usr/include/c++/v1/string:1938:44: error: 'basic_string<_CharT, _Traits, _Allocator> is missing exception specification 'noexcept(is_nothrow_copy_constructible<allocator_type>value)' basic_string<_CharT, _Traits, _Allocator>basic_string(const allocator_type& __a) ^ /usr/include/c++/v1/string:1326:40: note: previous declaration is here _LIBCPP_INLINE_VISIBILITY explicit basic_string(const allocator_type& __a)
You must fix this STL bug by editing the file /usr/include/c++/v1/string and after line 1939, put this patch,
template <class _CharT, class _Traits, class _Allocator> inline _LIBCPP_INLINE_VISIBILITY basic_string<_CharT, _Traits, _Allocator>basic_string(const allocator_type& __a) // Insert patch noexcept(is_nothrow_copy_constructible<allocator_type>::value_) // End of patch : __r_(__a) { #if _LIBCPP_DEBUG_LEVEL >= 2 __get_db()->__insert_c(this); #endif __zero(); }

If that doesn't work, change your makefiles by replacing the default library -stdlib=libc++, with the older GNU library -stdlib=libstdc++

# This is OK if we switch to the older GNU library. $ clang++ -std=c++11 -stdlib=libstdc++ foo.cpp

Windows You can use the C++ compiler in the Cygwin development tools or you can download Visual Studio Express and follow instructions.

C++ GUIs

I haven't tried them yet but the C++ GUIs FoxTookit and WxWidgets sound interesting. WxWidgets looks a lot like Windows API with classes. Here is a tutorial ending up with source code for a tetris game.

LLDB Debugger

I use the lldb debugger in the llvm toolchain which comes installed on Mac OS X.

# Load executable compiled with -g option. lldb Bin/Primpoly.exe (lldb) target create "Bin/Primpoly.exe" Current executable set to 'Bin/Primpoly.exe' (x86_64). # Set a breakpoint in the code. (lldb) b ppBigInt.cpp:433 Breakpoint 2: where = Primpoly.exe`BigInt::operator unsigned long() const + 18 at ppBigInt.cpp:433, address = 0x000000010000fa92 # Run the program. (lldb) run 2 4 Process 49251 launched: '/Users/seanoconnor/Desktop/Sean/WebSite/Mathematics/AbstractAlgebra/PrimitivePolynomials/Project/Build/Bin/Primpoly.exe' (x86_64) Primpoly Version 13.0 - A Program for Computing Primitive Polynomials. Copyright (C) 1999-2018 by Sean Erik O'Connor. All Rights Reserved. ... Process 49251 stopped * thread #1: tid = 0x8a8db2, 0x000000010000fa92 Primpoly.exe`BigInt::operator unsigned long(this=0x00007fff5fbf8d18) const + 18 at ppBigInt.cpp:433, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1 frame #0: 0x000000010000fa92 Primpoly.exe`BigInt::operator unsigned long(this=0x00007fff5fbf8d18) const + 18 at ppBigInt.cpp:433 430 BigInt::operator ppuint() const 431 throw( BigIntOverflow, bad_exception ) 432 { -> 433 ppuint result = 0 ; 434 ppuint b = base_() ; 435 436 for (int i = static_cast<unsigned int> digit_.size()) - 1 ; i > 0 ; --i) # Backtrace (lldb) bt * thread #1: tid = 0x8a8db2, 0x000000010000fa92 Primpoly.exe`BigInt::operator unsigned long(this=0x00007fff5fbf8d18) const + 18 at ppBigInt.cpp:433, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1 * frame #0: 0x000000010000fa92 Primpoly.exe`BigInt::operator unsigned long(this=0x00007fff5fbf8d18) const + 18 at ppBigInt.cpp:433 frame #1: 0x00000001000dc1b9 Primpoly.exe`unitTest() + 16233 at ppUnitTest.cpp:641 frame #2: 0x0000000100000c89 Primpoly.exe`main(argc=3, argv=0x00007fff5fbff600) + 89 at Primpoly.cpp:162 frame #3: 0x00007fff934cd5ad libdyld.dylib`start + 1 # Step to next line. (lldb) n Process 49251 stopped * thread #1: tid = 0x8a8db2, 0x000000010000fa9d Primpoly.exe`BigInt::operator unsigned long(this=0x00007fff5fbf8d18) const + 29 at ppBigInt.cpp:434, queue = 'com.apple.main-thread', stop reason = step over frame #0: 0x000000010000fa9d Primpoly.exe`BigInt::operator unsigned long(this=0x00007fff5fbf8d18) const + 29 at ppBigInt.cpp:434 431 throw( BigIntOverflow, bad_exception ) 432 { 433 ppuint result = 0 ; -> 434 ppuint b = base_() ; 435 436 for (int i = static_cast<unsigned int> digit_.size()) - 1 ; i > 0 ; --i) 437 { # List breakpoints. (lldb) br l Current breakpoints: 1: file = 'ppBigInt.cpp', line = 433, locations = 1, resolved = 1, hit count = 1 1.1: where = Primpoly.exe`BigInt::operator unsigned long() const + 18 at ppBigInt.cpp:433, address = 0x000000010000fa92, resolved, hit count = 1 2: name = 'l', locations = 0 (pending) 3: name = 'list', locations = 0 (pending) 4: name = 'l', locations = 0 (pending) # Delete all breakpoints. (lldb) br del About to delete all breakpoints, do you want to do that?: ÆY/nÅ y All breakpoints removed. (4 breakpoints) # Print a value. (lldb) p digit_ (std::__1::vector<unsigned long, std::__1::allocator<unsigned long> >) $4 = size=4 { [0] = 4 [1] = 3 [2] = 2 [3] = 1

Python

Go to Python and download the latest version of Python from the site. Go to custom and make sure it installs into the correct directories on the system.

Windows On a Windows system, install into C:\Program Files\Python Go to Start/Control Panel/System/Advanced/Environment Variables and add the string ;C:\Python31; to the end of the path. For cygwin, in your Bash profile in your home directory, /cygdrive/c/cygwin/home/.bashrc, put PATH="/cygdrive/c/Python31:${PATH}" or else you can do a symbolic link, cd /usr/bin ; rm python ; ln -s /cygdrive/c/Python31/python3 python"

Mac or Ubuntu Linux Place code in your .bash_profile to set the path to Python 3.x Then run python -V to check the version is 3x and type quit() to exit. Keep the tutorial and library reference handy. Or better yet, download the whole set.

If you program LISP already, Python is very similar.

Common Lisp

My parser generator project is coded in Common Lisp.

Macs Download the install package for the binary from Steel Bank Common Lisp. Unpack and follow instructions.

$ sudo INSTALL_ROOT=/usr/local sh install.sh ... SBCL has been installed: binary /usr/local/bin/sbcl core and contribs in /usr/local/lib/sbcl/

You'll get a bare-bones command line REPL. Try running $ sbcl from the command line and try (car '(a b c)) and (cdr '(a b)) and (quit) For convenience, I load my LISP parser generator project files in the SBCL startup file .sbclrc

(load "LR(1)AndLALR(1)ParserGenerator.lsp") (load "LR(1)AndLALR(1)Parser.lsp") (test-parser-generator) (test-parser)

However, your life will be much easier if youw download and install Slime Unpack and install the slime directory in /Applications/slime then install this .emacs file in your home directory:

(setq inferior-lisp-program "/usr/local/bin/sbcl") (add-to-list 'load-path "/Applications/slime") (require 'slime) (slime-setup)
VimEmacsSlime.
Vim, Ctags, Emacs, Slime, Steel Bank Common Lisp.

You should also install Emacs Invoke Emacs and with the command M-x slime go into Lisp interaction mode. Useful REPL commands are Control-c Control-p to move back one prompt, Control-c Control-n to move forward, Control-a and Control-e to go to beginning and end of the line, Control-x o to switch between buffers, Control-C Control-D d to lookup documentation for a symbol, and Control-X Control-S to exit emacs.

For LISP tutorials and references online see

I've also used CLISP

Perl

I was programming in Perl and switched over to Python after discovering this article.

FORTRAN

The first language I learned was FORTRAN from Bill Joy at U. C. Berkeley!

Berkeley Computing Center Punched card circa 1975.
Berkeley Computing Center Punched card circa 1975.

It now has object oriented programming!

$ cat circle.f90 ! A module containing classes. module class_Circle implicit none private real :: pi = 3.1415926535897931d0 ! Module-wide private constant ! Circle class. type, public :: Circle ! Member variable. real :: radius contains ! Member functions defined here but implemented elsewhere in the module. procedure :: area => circle_area procedure :: print => circle_print end type Circle ! Circle member functions implemented here. contains function circle_area( this ) result( area ) class( Circle ), intent( in ) :: this real :: area area = pi * this%radius ** 2 end function circle_area subroutine circle_print(this) class( Circle ), intent( in ) :: this real :: area area = this%area() ! Call the type-bound function print *, 'Circle: r = ', this%radius, ' area = ', area end subroutine circle_print end module class_Circle program circle_test use class_Circle ! Use the classes in the module. implicit none type( Circle ) :: c ! Declare a variable of type Circle. c = Circle( 1.5 ) ! Use the implicit constructor, radius = 1.5. call c%print ! Call the type-bound subroutine end program circle_test $ gfortran circle.f90 -o circle ./circle Circle: r = 1.50000000 area = 7.06858349

Octave

Octave is a free MATLAB clone.

Run Octave.app from /Applications. It comes up in a terminal window. In the window, cd to your working directory containing your MATLAB .m files. Type the name of your MATLAB function or type the name of the .m file if you have just an inline main program. Plots will pop up in a separate window using the X11 windowing system.

Sympy

Sympy is a symbolic math package build on top of python. Here's how to install on both Mac and Ubuntu Linux

First install mpmath by doing

git clone git://github.com/fredrik-johansson/mpmath cd mpmath git pull sudo python setup.py install
Then install sympy by doing
git clone git://github.com/sympy/sympy.git cd sympy git pull sudo python setup.py install
To do plotting, install matplotlib by doing,
python -mpip install -U matplotlib
Test by running Python, importing sympy and running a few commands,
python Python 3.5.2 (default, Nov 23 2017, 16:37:01) [GCC 5.4.0 20160609] on linux Type "help", "copyright", "credits" or "license" for more information. >>> from sympy import * >>> init_printing() >>> x = Symbol('x') >>> f = Function('f') >>> f = sin(x) >>> diff(f,x) cos(x) >>> integrate(f,x) -cos(x) >>> f.series(x,0,10) x - x**3/6 + x**5/120 - x**7/5040 + x**9/362880 + O(x**10) >>> a = Symbol('a') >>> b = Symbol('b') >>> c = Symbol('c') >>> d = Symbol('d') >>> m = Matrix( [[ a, b] , [c, d] ] ) >>> m ⎡a b⎤ ⎢ ⎥ ⎣c d⎦ > >>> m.eigenvals() ⎧ _________________________ _________________________ ⎫ ⎪ ╱ 2 2 ╱ 2 2 ⎪ ⎨a d ╲╱ a - 2⋅a⋅d + 4⋅b⋅c + d a d ╲╱ a - 2⋅a⋅d + 4⋅b⋅c + d ⎬ ⎪─ + ─ - ────────────────────────────: 1, ─ + ─ + ────────────────────────────: 1⎪ ⎩2 2 2 2 2 2 ⎭ >>> m * (m ** -1) ⎡ a⋅d b⋅c ⎤ ⎢───────── - ───────── 0 ⎥ ⎢a⋅d - b⋅c a⋅d - b⋅c ⎥ ⎢ ⎥ ⎢ a⋅d b⋅c ⎥ ⎢ 0 ───────── - ─────────⎥ ⎣ a⋅d - b⋅c a⋅d - b⋅c⎦ >>> simplify( m * (m ** -1)) ⎡1 0⎤ ⎢ ⎥ ⎣0 1⎦

And here's a simple plot,

>>> plot(x**2,(x,-1,1))
Ubuntu Display Settings Part 1.
Sympy plot( x**2, (x,-1,1))

EDITORS

VIM Programmer's Editor

Download gvim from Vim for Mac and Ubuntu Linux. Don't forget the Vim User Manual and tutorial.

In Ubuntu Linux do this:

sudo apt-get install vim sudo apt-get install vim-gnome

I've customized the Vim GUI in the startup file .vimrc which lives in the home directory ~ on Mac or Ubuntu Linux. On Windows the home directory is C:/Program Files/vim Here is my .vimrc

"============================================================================= " " NAME " " .vimrc " " DESCRIPTION " " Settings for Vim editor loaded upon startup. " " NOTES " " UNIX, Mac OS X: Copy to ~/.vimrc " Windows: Copy to C:/Program Files/vim/_vimrc " " AUTHOR " " Bram Moolenaar &lt;Bram@vim.org&gt; " Sean E. O'Connor 22 Apr 2017 " "============================================================================= " " Use Vim settings, rather then Vi settings (much better!). " This must be first, because it changes other options as a side effect. set nocompatible " Mouse and window behavior tuned for Microslime Windows. behave mswin " Restore Control-F to page forward instead of find dialog box. "unmap&lt;C-F&gt; " Allow backspacing over everything in insert mode. set backspace=indent,eol,start " Move backup files to ~/.vim/.backup " Create the backup directories if they don't exist. set backup if !isdirectory($HOME.".vim/.backup") silent! execute "!mkdir ~/.vim/.backup" endif set backupdir=~/.vim/.backup if !isdirectory($HOME.".vim/.swp") silent! execute "!mkdir ~/.vim/.swp" endif set directory=~/.vim/.swp set writebackup set history=500 " Keep 500 lines of command line history. set ruler " Show the cursor position all the time. set showcmd " Display incomplete commands. set incsearch " Do incremental searching. set hlsearch " Searches are highlighted. set wildmenu " File name completing using tabs cycles through all possibilities. set lazyredraw " Don't redraw screen while running macros. set lines=60 " Number of lines visible on the screen. set columns=140 " Number of columns visible on the screen. winpos 400 45 " Initial window position (MacBookPro). set winwidth=140 " Initial window width. set winminwidth=140 " set cursorline " Highlight the current line. set number " Show line numbers. set smartindent " Use smart indenting. set tabstop=4 " Tabs are 4 spaces wide. set softtabstop=4 " When editing, tabs are 4 spaces wide. set shiftwidth=4 " Indent 4 spaces. set expandtab " Expand tabs into spaces. set smarttab set showmatch " Show matching parentheses. set matchtime=5 " Match time is 1/2 sec. " Error blink and bell. set errorbells set visualbell " Set file paths to my most commonly used directories (MacBookPro). set path="~/Applications/vim/**" set path+="~/Desktop/Sean/WebSite/**" " Default directory is my current active subdirectory " in my web page directory. " This is where :e . takes us. cd ~/Desktop/Sean/WebSite/Mathematics/AbstractAlgebra/PrimitivePolynomials/Project/SourceCode " Set paths for tags files generated by Ctags. See :help tags " "./tags" means search for the file "tags" in the same directory as the current f i l e you are editing. " "tags" means search for the file "tags" in the current working directory (the directory shown by the command :pwd) " NOTE: this is affected if you set autochdir (see below). " Then search from the directory containing tags to your home directory. set tags=./tags,tags;~ " Automatically change the current working directory to the one containing the file which was opened. See :help autochdir "Note: When this option is on some plugins may not work. set autochdir " Color them the same as the C group-name "Type". highlight link xType Type " =============================================================================== " | Use the Torte color scheme for the full gVim GUI but " | default for launching vim from a command window. " =============================================================================== if has("gui_running") " Use the Torte color scheme when we run vim in GUI mode. " Otherwise if launched from the terminal, use the default colors. colorscheme torte if has("gui_gtk2") set guifont=Courier\ New\ 11 elseif has("gui_kde") echoerr "Sorry, please look into your kvim documentation" elseif has("x11") set guifont=-*-courier-medium-r-normal-*-*-180-*-*-m-*-* elseif has("gui_win32") set guifont=Courier_New:h13:b "set guifont=Lucida_Console:h13:cDEFAULT elseif has("gui_mac") "set guifont=* else "set guifont=* endif endif " Switch syntax highlighting on, when the terminal has colors. " Also switch on highlighting the last used search pattern. if &t_Co &gt; 2 || has("gui_running") syntax on set hlsearch endif " Abbreviations. :iabbrev soc Sean E. O'Connor " Insert html math symbols. map ,th i&lt;em&gt;Theorem.&lt;/em&gt; map ,pf i&lt;em&gt;Proof.&lt;/em&gt; map ,lem i&lt;em&gt;Lemma.&lt;/em&gt; map ,cor i&lt;em&gt;Corollary.&lt;/em&gt; map ,qed $\blacksquare$ " Insert html emphasis. map ,em i&lt;em&gt;&lt;/em&gt;&lt;ESC&gt;4hi " Insert html paragraph. map ,par i&lt;p&gt;&lt;CR&gt;&lt;CR&gt;&lt;/p&gt;&lt;ESC&gt;ki " Insert a scroll box. map ,sb i&lt;div class="scrollBox"&gt; &lt;div class="scrollBoxContent"&gt;&lt;CR&gt;&lt;CR&gt;&lt;/div&gt;&lt;/div&gt;&lt;ESC&gt;ki " Reload this VIM resource file. map ,sou :source $HOME/.vimrc&lt;CR&gt; " Save the file. map ,sa :w&lt;CR&gt; " Trim blanks at end of all lines. Turn off search highlighting. map ,tbe :%s/\s*$//&lt;CR&gt;&lt;ESC&gt;:nohlsearch&lt;CR&gt;&lt;ESC&gt; " Make p in Visual mode replace the selected text with the "" register. vnoremap p &lt;Esc&gt;:let current_reg = @"&lt;CR&gt;gvs&lt;C-R&gt;=current_reg&lt;CR&gt;&lt;Esc&gt; " Turn off highlighting after searches. map ,&lt;SPACE&gt; :nohlsearch&lt;CR&gt; " Toggle comment lines. You can highlight the range " with the mouse and then type ,to map ,to :call ToggleComment()&lt;CR&gt; function! ToggleComment() " Get the line under the cursor " Check if it begins with | if getline(".") =~ '^|' let s:savedSearchPat=@/ s/^|// let @/=s:savedSearchPat else let s:savedSearchPat=@/ s/^/|/ let @/=s:savedSearchPat endif endfunction map ,ch :call CleanHTML()&lt;CR&gt; function! CleanHTML() s/&gt;/\&gt;/g s/&lt;/\&lt;/g s/&/\&amp;/g endfunction " Convert C comments on a line to C++ comments. map ,cc :call ConvertCCommentToCpp()&lt;CR&gt; function! ConvertCCommentToCpp() " Get the line under the cursor " Check if contains a /* if getline(".") =~ '\/\*' let s:savedSearchPat=@/ s/\/\*/\/\// let @/=s:savedSearchPat endif if getline(".") =~ '\*\/' let s:savedSearchPat=@/ s/\*\/// let @/=s:savedSearchPat endif endfunction " Justify a paragraph. map ,j gqap
Installing Ctags

Universal Ctags is a successor to Exuberant Ctags. To install it on Mac first install Brew then install ctags,

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" brew install --HEAD universal-ctags/universal-ctags/universal-ctags

Then add to your path in .bash_profile,

ctagspath="/usr/local/Cellar/universal-ctags/HEAD-4fe1a60/bin" PATH="${ctagspath}:${PATH}" export PATH

To install it on Ubuntu Linux first install brew, and automake,

sudo apt install linuxbrew-wrapper brew doctor sudo apt-get install automake sudo apt-get update

then download universal ctags, configure, build and install,

git clone https://github.com/universal-ctags/ctags.git cd ctags/ ./configure make sudo make install
Searching Multiple Files in Vim

For example, to search all *.cpp files in the current directory for the word static_cast, do this on the vim command line:

:vimgrep /static_cast/ **/*.cpp :copen 20

You can then go to the line containing the pattern you want and hit return to go to the file.

Searching Patterns in Multiple Files in Vim
File Differences in Vim
To do file differences, load the first file, ignore whitespace with :diffopt=iwhite, then do :diffsplit <second file name>. :diffupdate will resync if needed.

GIMP Paint Program

Similar to Adobe Photoshop with paint tools and layers, but free, GIMP runs on both Windows, Mac, and Ubuntu Linux download it and its documentation from Gimp

In Ubuntu Linux install with

sudo apt-get install gimp

Blender

Blender is a free 3D rendering and animation tool. I have a worked example on my art page.

Inkscape

I use Inkscape to do the drawing and generate SVG files.

Open Office Suite

Free office suite software set with spreadsheets, presentation software, word processing, and drawing from Open Office.

ΤεΧ

Install from MacTex TeX User's Group, and be sure to read the excellent A (Not So) Short Introduction to LATEX2ε

INTERNET

Netzero ISP

I used connect to the internet through the dialup telephone line using Netzero but now I use AT&T Uverse.

FTP

I use the FTP client Transmit. Not free but very easy to use.

Zip File Compression Software Windows

Install PKZIP from PKWare or use Gzip

Web Browsers

Download the Firefox, Opera and Chromium browsers.

GAMES

Stellarium

Stellarium is a free planetarium program.

Red Pill Screen Saver

Get the source code from GitHub opx3 / RedPill2. You can clone the repository using

git clone https://github.com/opx3/RedPill2.git

Build in XCode in Release mode. Let XCode update your settings. Double click on RedPill.saver to load it.


Copyright © 1999-2018 by Sean Erik O'Connor. All Rights Reserved.     last updated 23 Apr 18.