CtrlK
BlogDocsLog inGet started
Tessl Logo

pantheon-ai/makefile-generator

Generate GNU Make build systems that define build targets, configure dependencies, set up phony targets, and implement parallel builds. Use when creating make/Makefile/.mk files, implementing compile rules, or building production-ready build automation for C/C++, Go, Python, and Java projects.

Overall
score

93%

Does it follow best practices?

Validation for skill structure

Overview
Skills
Evals
Files

generate_makefile_template.shscripts/

#!/usr/bin/env bash
#
# generate_makefile_template.sh
# Description: Generate Makefile templates for different project types
# Usage: bash generate_makefile_template.sh [OPTIONS] [PROJECT_TYPE] [PROJECT_NAME] [OUTPUT_FILE]
#

set -euo pipefail

# Script metadata
readonly SCRIPT_NAME="$(basename "${BASH_SOURCE[0]}")"
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

# Default values
FORCE=0
PROJECT_TYPE=""
PROJECT_NAME="myproject"
OUTPUT_FILE="Makefile"

# Colors for output
if [[ -t 1 ]]; then
    readonly RED='\033[0;31m'
    readonly GREEN='\033[0;32m'
    readonly YELLOW='\033[1;33m'
    readonly NC='\033[0m' # No Color
else
    readonly RED=''
    readonly GREEN=''
    readonly YELLOW=''
    readonly NC=''
fi

# Print functions
print_error() {
    echo -e "${RED}ERROR:${NC} $*" >&2
}

print_success() {
    echo -e "${GREEN}SUCCESS:${NC} $*"
}

print_info() {
    echo -e "${YELLOW}INFO:${NC} $*"
}

# Usage information
usage() {
    cat << EOF
Usage: ${SCRIPT_NAME} [OPTIONS] [PROJECT_TYPE] [PROJECT_NAME] [OUTPUT_FILE]

Generate Makefile templates for different project types.

Options:
    -f, --force     Overwrite existing files without prompting
    -h, --help      Show this help message

Arguments:
    PROJECT_TYPE    Type of project (required)
    PROJECT_NAME    Name of the project (default: myproject)
    OUTPUT_FILE     Output file path (default: Makefile)

Project Types:
    c               Simple C project
    c-lib           C library project
    cpp             C++ project
    go              Go project
    python          Python project
    java            Java project
    generic         Generic project

Examples:
    ${SCRIPT_NAME} c myapp
    ${SCRIPT_NAME} go server Makefile
    ${SCRIPT_NAME} python mypackage build.mk
    ${SCRIPT_NAME} -f c myapp Makefile  # Force overwrite

EOF
}

# Parse command line options
parse_args() {
    while [[ $# -gt 0 ]]; do
        case "$1" in
            -f|--force)
                FORCE=1
                shift
                ;;
            -h|--help)
                usage
                exit 0
                ;;
            -*)
                print_error "Unknown option: $1"
                usage
                exit 1
                ;;
            *)
                # First positional argument is PROJECT_TYPE
                if [[ -z "${PROJECT_TYPE}" ]]; then
                    PROJECT_TYPE="$1"
                elif [[ "${PROJECT_NAME}" == "myproject" ]]; then
                    PROJECT_NAME="$1"
                else
                    OUTPUT_FILE="$1"
                fi
                shift
                ;;
        esac
    done
}

# Generate C project Makefile
generate_c_makefile() {
    local project="$1"
    sed "s/PROJECT_NAME/${project}/g" << 'EOF'
# Makefile for C project
SHELL := bash
.ONESHELL:
.SHELLFLAGS := -eu -o pipefail -c
.DELETE_ON_ERROR:
.SUFFIXES:
MAKEFLAGS += --warn-undefined-variables
MAKEFLAGS += --no-builtin-rules

PROJECT := PROJECT_NAME
VERSION := 1.0.0

CC ?= gcc
CFLAGS ?= -Wall -Wextra -O2
PREFIX ?= /usr/local

SRCDIR := src
BUILDDIR := build
OBJDIR := $(BUILDDIR)/obj

SOURCES := $(wildcard $(SRCDIR)/*.c)
OBJECTS := $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o)
DEPENDS := $(OBJECTS:.o=.d)
TARGET := $(BUILDDIR)/$(PROJECT)

.PHONY: all clean install test help

## Build the application (default)
all: $(TARGET)

$(TARGET): $(OBJECTS)
	@mkdir -p $(@D)
	$(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@

$(OBJDIR)/%.o: $(SRCDIR)/%.c
	@mkdir -p $(@D)
	$(CC) $(CPPFLAGS) $(CFLAGS) -MMD -MP -c $< -o $@

-include $(DEPENDS)

## Install to PREFIX
install: $(TARGET)
	install -d $(DESTDIR)$(PREFIX)/bin
	install -m 755 $(TARGET) $(DESTDIR)$(PREFIX)/bin/$(PROJECT)

## Run tests
test: $(TARGET)
	@echo "Running tests..."
	@$(TARGET) --test

## Remove build artifacts
clean:
	$(RM) -r $(BUILDDIR)

## Show help
help:
	@echo "$(PROJECT) v$(VERSION)"
	@echo ""
	@sed -n 's/^## //p' $(MAKEFILE_LIST)
EOF
}

# Generate C library Makefile
generate_c_lib_makefile() {
    local project="$1"
    sed "s/PROJECT_NAME/${project}/g" << 'EOF'
# Makefile for C library project
.DELETE_ON_ERROR:

PROJECT := PROJECT_NAME
VERSION := 1.0.0

CC ?= gcc
AR ?= ar
RANLIB ?= ranlib
CFLAGS ?= -Wall -Wextra -O2 -fPIC
PREFIX ?= /usr/local

SRCDIR := src
BUILDDIR := build
OBJDIR := $(BUILDDIR)/obj

SOURCES := $(wildcard $(SRCDIR)/*.c)
OBJECTS := $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o)
HEADERS := $(wildcard $(SRCDIR)/*.h)

STATIC_LIB := $(BUILDDIR)/lib$(PROJECT).a
SHARED_LIB := $(BUILDDIR)/lib$(PROJECT).so.$(VERSION)

.PHONY: all static shared clean install help

## Build both static and shared libraries
all: static shared

## Build static library
static: $(STATIC_LIB)

## Build shared library
shared: $(SHARED_LIB)

$(STATIC_LIB): $(OBJECTS)
	@mkdir -p $(@D)
	$(AR) rcs $@ $^
	$(RANLIB) $@

$(SHARED_LIB): $(OBJECTS)
	@mkdir -p $(@D)
	$(CC) -shared -Wl,-soname,lib$(PROJECT).so.1 $^ -o $@

$(OBJDIR)/%.o: $(SRCDIR)/%.c
	@mkdir -p $(@D)
	$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@

## Install libraries
install: all
	install -d $(DESTDIR)$(PREFIX)/lib
	install -m 644 $(STATIC_LIB) $(DESTDIR)$(PREFIX)/lib/
	install -m 755 $(SHARED_LIB) $(DESTDIR)$(PREFIX)/lib/
	install -d $(DESTDIR)$(PREFIX)/include/$(PROJECT)
	install -m 644 $(HEADERS) $(DESTDIR)$(PREFIX)/include/$(PROJECT)/

## Clean build artifacts
clean:
	$(RM) -r $(BUILDDIR)

## Show help
help:
	@sed -n 's/^## //p' $(MAKEFILE_LIST)
EOF
}

# Generate Go project Makefile
generate_go_makefile() {
    local project="$1"
    sed "s/PROJECT_NAME/${project}/g" << 'EOF'
# Makefile for Go project
.PHONY: all build install test clean help

PROJECT := PROJECT_NAME
GO ?= go
PREFIX ?= /usr/local

VERSION := $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
LDFLAGS := -ldflags "-X main.version=$(VERSION)"

SOURCES := $(shell find . -name '*.go' -not -path './vendor/*')
TARGET := $(PROJECT)

## Build the application (default)
all: build

## Build binary
build: $(TARGET)

$(TARGET): $(SOURCES) go.mod go.sum
	$(GO) build $(LDFLAGS) -o $@ ./cmd/$(PROJECT)

## Install to PREFIX
install: $(TARGET)
	install -d $(DESTDIR)$(PREFIX)/bin
	install -m 755 $(TARGET) $(DESTDIR)$(PREFIX)/bin/

## Run tests
test:
	$(GO) test -v ./...

## Clean build artifacts
clean:
	$(RM) $(TARGET)
	$(GO) clean

## Format code
fmt:
	$(GO) fmt ./...

## Run linter
lint:
	golangci-lint run

## Show help
help:
	@echo "$(PROJECT) - Go Project"
	@echo "Version: $(VERSION)"
	@echo ""
	@sed -n 's/^## //p' $(MAKEFILE_LIST)
EOF
}

# Generate Python project Makefile
generate_python_makefile() {
    local project="$1"
    sed "s/PROJECT_NAME/${project}/g" << 'EOF'
# Makefile for Python project
.PHONY: all build install test clean help

PROJECT := PROJECT_NAME
PYTHON ?= python3
PIP ?= $(PYTHON) -m pip

## Build the package (default)
all: build

## Build Python package
build:
	$(PYTHON) -m build

## Install package
install:
	$(PIP) install .

## Install in development mode
develop:
	$(PIP) install -e .[dev]

## Run tests
test:
	$(PYTHON) -m pytest tests/ -v

## Format code
format:
	$(PYTHON) -m black src/ tests/
	$(PYTHON) -m isort src/ tests/

## Run linters
lint:
	$(PYTHON) -m flake8 src/ tests/
	$(PYTHON) -m pylint src/

## Clean build artifacts
clean:
	$(RM) -r build/ dist/ *.egg-info/
	$(RM) -r .pytest_cache/ .coverage htmlcov/
	find . -type d -name '__pycache__' -exec rm -r {} +

## Show help
help:
	@echo "$(PROJECT) - Python Project"
	@echo ""
	@sed -n 's/^## //p' $(MAKEFILE_LIST)
EOF
}

# Generate Java project Makefile
generate_java_makefile() {
    local project="$1"
    sed "s/PROJECT_NAME/${project}/g" << 'EOF'
# Makefile for Java project
.DELETE_ON_ERROR:

PROJECT := PROJECT_NAME
VERSION := 1.0.0
MAIN_CLASS := Main

# Java tools
JAVAC ?= javac
JAVA ?= java
JAR ?= jar

# Compiler flags
JAVAC_FLAGS ?= -Xlint:all -encoding UTF-8
JAR_FLAGS := cvf

# Directories
SRCDIR := src
BUILDDIR := build
CLASSDIR := $(BUILDDIR)/classes
LIBDIR := lib
DOCDIR := $(BUILDDIR)/docs
DISTDIR := dist

# Source and class files
JAVA_SOURCES := $(shell find $(SRCDIR) -name '*.java')
JAVA_CLASSES := $(JAVA_SOURCES:$(SRCDIR)/%.java=$(CLASSDIR)/%.class)

# Classpath
CLASSPATH := $(CLASSDIR)
ifneq ($(wildcard $(LIBDIR)/*.jar),)
    CLASSPATH := $(CLASSPATH):$(LIBDIR)/*
endif

# Output JAR
TARGET := $(DISTDIR)/$(PROJECT)-$(VERSION).jar

.PHONY: all build compile jar run test clean distclean docs help

## Build the project (default)
all: build

## Compile and create JAR
build: jar

## Compile Java sources
compile: $(CLASSDIR)/.compiled

$(CLASSDIR)/.compiled: $(JAVA_SOURCES)
	@mkdir -p $(CLASSDIR)
	$(JAVAC) $(JAVAC_FLAGS) -d $(CLASSDIR) -cp "$(CLASSPATH)" $(JAVA_SOURCES)
	@touch $@

## Create JAR file
jar: compile
	@mkdir -p $(DISTDIR)
	@echo "Manifest-Version: 1.0" > $(BUILDDIR)/MANIFEST.MF
	@echo "Main-Class: $(MAIN_CLASS)" >> $(BUILDDIR)/MANIFEST.MF
	@echo "Created-By: Makefile" >> $(BUILDDIR)/MANIFEST.MF
	$(JAR) $(JAR_FLAGS)m $(TARGET) $(BUILDDIR)/MANIFEST.MF -C $(CLASSDIR) .
	@echo "Created: $(TARGET)"

## Run the application
run: compile
	$(JAVA) -cp "$(CLASSPATH)" $(MAIN_CLASS)

## Run with JAR
run-jar: jar
	$(JAVA) -jar $(TARGET)

## Run tests (requires JUnit in lib/)
test: compile
	@echo "Running tests..."
	@if [ -d "$(SRCDIR)/test" ]; then \
		$(JAVA) -cp "$(CLASSPATH):$(LIBDIR)/*" org.junit.runner.JUnitCore; \
	else \
		echo "No test directory found"; \
	fi

## Generate Javadoc
docs:
	@mkdir -p $(DOCDIR)
	javadoc -d $(DOCDIR) -sourcepath $(SRCDIR) -subpackages .
	@echo "Documentation generated: $(DOCDIR)/index.html"

## Remove build artifacts
clean:
	$(RM) -r $(BUILDDIR)
	@echo "Clean complete"

## Remove all generated files
distclean: clean
	$(RM) -r $(DISTDIR)
	@echo "Distclean complete"

## Show available targets
help:
	@echo "$(PROJECT) v$(VERSION) - Java Project"
	@echo ""
	@echo "Targets:"
	@sed -n 's/^## //p' $(MAKEFILE_LIST) | column -t -s ':' | sed 's/^/  /'
	@echo ""
	@echo "Variables:"
	@echo "  JAVAC=$(JAVAC)"
	@echo "  JAVAC_FLAGS=$(JAVAC_FLAGS)"
	@echo "  MAIN_CLASS=$(MAIN_CLASS)"
	@echo ""
	@echo "Directory Structure:"
	@echo "  $(SRCDIR)/          - Java source files"
	@echo "  $(LIBDIR)/          - External JAR dependencies"
	@echo "  $(BUILDDIR)/        - Build output"
	@echo "  $(DISTDIR)/         - Distribution JARs"
	@echo ""
	@echo "Examples:"
	@echo "  make                    # Build the project"
	@echo "  make run                # Compile and run"
	@echo "  make jar                # Create JAR file"
	@echo "  make MAIN_CLASS=MyApp   # Set main class"
EOF
}

# Generate generic Makefile
generate_generic_makefile() {
    local project="$1"
    sed "s/PROJECT_NAME/${project}/g" << 'EOF'
# Generic Makefile
.PHONY: all build clean install test help

PROJECT := PROJECT_NAME
VERSION := 1.0.0

## Build the project (default)
all: build

## Build target
build:
	@echo "Building $(PROJECT)..."
	# Add build commands here

## Install target
install: build
	@echo "Installing $(PROJECT)..."
	# Add installation commands here

## Run tests
test:
	@echo "Running tests..."
	# Add test commands here

## Clean build artifacts
clean:
	@echo "Cleaning..."
	# Add cleanup commands here

## Show help
help:
	@echo "$(PROJECT) v$(VERSION)"
	@echo ""
	@echo "Available targets:"
	@sed -n 's/^## //p' $(MAKEFILE_LIST)
EOF
}

# Main generation logic
generate_makefile() {
    local type="$1"
    local project="$2"
    local output="$3"

    # Check if output file exists
    if [[ -f "${output}" ]]; then
        if [[ "${FORCE}" -eq 1 ]]; then
            print_info "Overwriting existing file: ${output}"
        elif [[ -t 0 ]]; then
            # Interactive mode - prompt user
            print_error "File '${output}' already exists"
            read -p "Overwrite? [y/N] " -n 1 -r
            echo
            if [[ ! $REPLY =~ ^[Yy]$ ]]; then
                print_info "Aborted"
                exit 0
            fi
        else
            # Non-interactive mode - fail without --force
            print_error "File '${output}' already exists. Use -f/--force to overwrite."
            exit 1
        fi
    fi

    print_info "Generating ${type} Makefile for project '${project}'..."

    case "${type}" in
        c)
            generate_c_makefile "${project}" > "${output}"
            ;;
        c-lib)
            generate_c_lib_makefile "${project}" > "${output}"
            ;;
        cpp)
            generate_c_makefile "${project}" | sed 's/gcc/g++/g; s/\.c/.cpp/g' > "${output}"
            ;;
        go)
            generate_go_makefile "${project}" > "${output}"
            ;;
        python)
            generate_python_makefile "${project}" > "${output}"
            ;;
        java)
            generate_java_makefile "${project}" > "${output}"
            ;;
        generic)
            generate_generic_makefile "${project}" > "${output}"
            ;;
        *)
            print_error "Unknown project type: ${type}"
            echo ""
            usage
            exit 1
            ;;
    esac

    print_success "Makefile generated: ${output}"
    print_info "Edit the Makefile to customize for your project"
    print_info "Run 'make help' to see available targets"
}

# Parse arguments
parse_args "$@"

# Check required argument
if [[ -z "${PROJECT_TYPE}" ]]; then
    usage
    exit 0
fi

# Main execution
generate_makefile "${PROJECT_TYPE}" "${PROJECT_NAME}" "${OUTPUT_FILE}"

Install with Tessl CLI

npx tessl i pantheon-ai/makefile-generator@0.1.0

SKILL.md

tile.json