Skip to content
schernetsky edited this page Mar 16, 2015 · 7 revisions

Inspiration:

There are a lot of powerful open source libraries available written in C/C++, and I like to take advantage of these.

In recent past, each library I added came with baggage. Since I was building for iOS and Android, each one had to be cross-compiled because sadly there is no apt-get install libpng on mobile platforms. The problem was: there was no automation for building these external libraries.

I began looking at the tools that were available and stumbled upon cmoss, a collection of scripts for building various open source libraries for iOS and Android. It was a good start, but it needed a bit of tweaking and it was a hassle to add libraries not on the list, and also maintaining a collection of bash scripts is not the most elegant solution. This was when the idea for AmigoMake was born.

What is AmigoMake:

AmigoMake is the build system we use here at AmigoCloud, both for external libraries and our own code. When I began writing AmigoMake the initial idea was just to automate and manage external libraries, but then it became clear how I could integrate the system with our own code as well.

Before, I had makefiles in multiple directories that explicitly defined all the source files. Adding or removing files in our code base was a tad annoying. I thought about how I never had this issue with my Java code: just add a new class and the IDE handles it. I wanted AmigoMake to work in a similar fashion: I only want one makefile where I specify dependencies and build parameters and not list all the files in a directory that end in .c or .cpp. AmigoMake will find the source files and build a header dependency tree, and then it will call clang/gcc for you with the correct flags. In fact, there's no need to stick to the include/src directories. AmigoMake will correctly resolve regardless of the directory structure, and additionally has an option to export all the headers.

For Android in particular this made my life a lot easier. I could now integrate most open source libraries with a few lines of python code using the NDK standalone toolchain. I no longer had to search how to build a particular library for Android and find/write a proper Android.mk; instead, I could now easily cross-compile anything compatible, and have a shiny new shared library within minutes!

Features at a Glance

  • C/C++ Code Compilation
    • gcc and clang support
    • iOS, Android, x86 target support
    • header dependency tree(recompile only updated files)
  • External Libraries
    • easy to wrap make, cmake, and others
    • dependency handling
    • multiple libraries can be merged into one static or shared lib
    • included examples
      • proj4, libpng, libjpeg, gmock
      • sqlite, freetype, minizip, bzip
      • openssl, cURL, libicu, boost
  • Makefiles written in Python
    • AmigoMake packages specify a directory
      • no need to add individual files
      • support to exclude sub-directories and files
    • Import custom python code if needed
  • Easily Extensible

Samples:

The following would allow the user to build a static library from all C/C++ files (recursively located) in the directory where the makefile is placed.

# AmigoMakefile
from cpackage import CPackage

def init(platform, params):
	global package
	package = CPackage('./', CPackage.STATIC_LIB, 'Test')
def build(platform, params):
	package.build(platform)
def clean(platform, params):
	package.clean(platform)

There are two actions supported in this example: build, and clean

Another more complex multi-platform example with libpng as a dependency and support for the --all flag:

from cpackage import CPackage
from ios_platform import IOSPlatform
from x86_platform import X86Platform
from android_platform import AndroidPlatform
import packages

def configure(params):
	pass

def init(platform, params):
	global package

	installdir = "../external/install/" + platform.unique_name() + "/"

	png = packages.Png("1.2.53", params.rootdir)
	png.set_install_dir(platform, installdir)

	lib_type = CPackage.SHARED_LIB
	if isinstance(platform, IOSPlatform):
		lib_type = CPackage.STATIC_LIB

	package = CPackage('./', lib_type, 'TestPackage')
	package.set_deps([png])

def build(platform, params):
	package.should_build_deps(params.all)
	package.build(platform)

def clean(platform, params):
	package.clean(platform, params.all)

External Library

from external_cpackage import ExternalCPackage
    
class LibPNG(ExternalCPackage):
	def __init__(self, version, rootdir):
    	super(LibPNG, self).__init__(version, rootdir)
    	self.set_zip_name("libpng-" + version + ".tar.gz")
    	self.set_url("ftp://ftp.simplesystems.org/" +
    			 	 "pub/libpng/png/src/libpng12/" +
                 	 self.zip_name())

The following would the need to be added to the makefile:

png = LibPNG("1.2.53", params.rootdir)
png.set_install_dir(platform, package.install_dir(platform))
package.set_deps([png])

By default all depencies are always built. To use the --all flag the following line needs to be added:

package.should_build_deps(params.all)