Yet another compiling issue thread

Post Reply
segfault
Posts: 3
Joined: Thu Mar 07, 2019 8:08 pm

Yet another compiling issue thread

Post by segfault » Thu Mar 07, 2019 8:54 pm

Hello! Just for fun I decided I wanted to try and program GBA games, installed devkitpro, and started following the Tonc tutorials.
I've run into a bit of a snag when compiling and linking with libtonc, it seems like the compiler isn't seeing any symbols besides inline functions and macros defined in tonc's .h files.

Here's the full C program I'm trying to compile (only 1 file, uses tonc's irq and input functions, as well as typedefs):

Code: Select all

#include <tonc.h>

// demo.c
#define CANVAS_X 8
#define CANVAS_Y 5
#define CANVAS_WIDTH 64
#define CANVAS_HEIGHT 64

#define PALETTE_COUNT 7

COLOR palette[PALETTE_COUNT];
int cur_x = 0;
int cur_y = 0;
int color_index = 0;
unsigned int frames = 0;

void init(void)
{
	// setup interrupts
	irq_init(NULL);
	irq_add(II_VBLANK, NULL);
	
	// init screen
	REG_DISPCNT = DCNT_MODE3 | DCNT_BG2;
	
	// init palette
	palette[0] = RGB8(255, 0  , 0  );
	palette[1] = RGB8(255, 127, 0  );
	palette[2] = RGB8(255, 255, 0  );
	palette[3] = RGB8(0  , 255, 0  );
	palette[4] = RGB8(0  , 0  , 255);
	palette[5] = RGB8(75 , 0  , 130);
	palette[6] = RGB8(148, 0  , 211);
}

int wrap_on_keys(int cur, u32 key_up, u32 key_down, int min, int max)
{
	if (key_hit(key_up))
	{
		cur++;
		if (cur > max)
		{
			return min;
		}
	}
	else if (key_hit(key_down))
	{
		cur--;
		if (cur < min)
		{
			return max;
		}
	}
	
	return cur;
}

void update(void)
{
	key_poll();
	
	if (key_hit(KEY_A))
	{
		// fill pixel
		m3_mem[CANVAS_Y + cur_y][CANVAS_X + cur_x] = palette[color_index];
	}
	else if (key_hit(KEY_B))
	{
		// erase pixel
		m3_mem[CANVAS_Y + cur_y][CANVAS_X + cur_x] = CLR_BLACK;
	}
	
	color_index = wrap_on_keys(color_index, KEY_R, KEY_L, 0, PALETTE_COUNT - 1);
	cur_x = wrap_on_keys(cur_x, KEY_RIGHT, KEY_LEFT, 0, CANVAS_WIDTH - 1);
	cur_y = wrap_on_keys(cur_y, KEY_DOWN, KEY_UP, 0, CANVAS_HEIGHT - 1);
}

void render(void)
{
	// render simple palette GUI
	int i;
	COLOR selcol;
	
	for(i = 0; i < PALETTE_COUNT; i++)
	{
		// selection pixel
		if (i == color_index)
		{
			selcol = CLR_WHITE;
		}
		else
		{
			selcol = CLR_BLACK;
		}
		m3_mem[2 + (i * 2)][2] = selcol;
		
		// color pixel
		m3_mem[2 + (i * 2)][3] = palette[i];
	}
	
	// render pointer guides and canvas box
	
	// box top + bottom
	for(i = 0; i < CANVAS_WIDTH; i++)
	{
		// pointer x guide
		if (i == cur_x)
		{
			m3_mem[CANVAS_Y - 3][CANVAS_X + i] = CLR_WHITE;
		}
		else
		{
			m3_mem[CANVAS_Y - 3][CANVAS_X + i] = CLR_BLACK;
		}
		
		m3_mem[CANVAS_Y - 1][CANVAS_X + i] = CLR_WHITE;
		m3_mem[CANVAS_Y + CANVAS_HEIGHT][CANVAS_X + i] = CLR_WHITE;
	}
	// box left + right
	for(i = 0; i < CANVAS_HEIGHT; i++)
	{
		// pointer y guide
		if (i == cur_y)
		{
			m3_mem[CANVAS_Y + i][CANVAS_X - 3] = CLR_WHITE;
		}
		else
		{
			m3_mem[CANVAS_Y + i][CANVAS_X - 3] = CLR_BLACK;
		}
		
		m3_mem[CANVAS_Y + i][CANVAS_X - 1] = CLR_WHITE;
		m3_mem[CANVAS_Y + i][CANVAS_X + CANVAS_WIDTH] = CLR_WHITE;
	}
}

int main(void)
{
	init();
	
	// game loop
	for (;;)
	{
		VBlankIntrWait();
		update();
		render();
		frames++;
	}
	
	return 0;
}
Here is the simple makefile I'm using (I'm not very good at writing makefiles, so I might have done some things wrong):

Code: Select all

ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
endif

include $(DEVKITARM)/gba_rules

ARMPREFIX = arm-none-eabi-
CC = $(ARMPREFIX)gcc
OBJCOPY = $(ARMPREFIX)objcopy

PROJ = demo

SRC = *.c
OBJS = $(SRC:.c=.o)

INCLUDES = -I$(DEVKITPRO)/libtonc/include
LIBS = -L$(DEVKITPRO)/libtonc/lib -ltonc

STEP1ARGS = -mthumb-interwork -mthumb -O2 -Wall -fno-strict-aliasing $(INCLUDES) $(LIBS)
STEP2ARGS = -mthumb-interwork -mthumb -specs=gba.specs $(LIBS)

.PHONY: all
all:
	$(CC) $(STEP1ARGS) -c $(SRC)
	$(CC) $(STEP2ARGS) $(OBJS) -o $(PROJ).elf
	$(OBJCOPY) -v -O binary $(PROJ).elf $(PROJ).gba
	gbafix $(PROJ).gba

.PHONY: clean
clean:
	rm -f $(PROJ).elf
	rm -f $(PROJ).o
	rm -f $(PROJ).gba
And finally, the errors I get when running make:

Code: Select all

>  make
arm-none-eabi-gcc -mthumb-interwork -mthumb -O2 -Wall -fno-strict-aliasing -I/opt/devkitpro/libtonc/include -L/opt/devkitpro/libtonc/lib -ltonc -c *.c
arm-none-eabi-gcc -mthumb-interwork -mthumb -specs=gba.specs -L/opt/devkitpro/libtonc/lib -ltonc *.o -o demo.elf
/opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/8.2.0/../../../../arm-none-eabi/bin/ld: demo.o: in function `init':
demo.c:(.text+0x4): undefined reference to `irq_init'
/opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/8.2.0/../../../../arm-none-eabi/bin/ld: demo.c:(.text+0xc): undefined reference to `irq_add'
/opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/8.2.0/../../../../arm-none-eabi/bin/ld: demo.o: in function `wrap_on_keys':
demo.c:(.text+0x88): undefined reference to `__key_prev'
/opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/8.2.0/../../../../arm-none-eabi/bin/ld: demo.c:(.text+0x8c): undefined reference to `__key_curr'
/opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/8.2.0/../../../../arm-none-eabi/bin/ld: demo.o: in function `update':
demo.c:(.text+0x94): undefined reference to `key_poll'
/opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/8.2.0/../../../../arm-none-eabi/bin/ld: demo.c:(.text+0x164): undefined reference to `__key_prev'
/opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/8.2.0/../../../../arm-none-eabi/bin/ld: demo.c:(.text+0x168): undefined reference to `__key_curr'
/opt/devkitpro/devkitARM/bin/../lib/gcc/arm-none-eabi/8.2.0/../../../../arm-none-eabi/bin/ld: demo.o: in function `main':
demo.c:(.text.startup+0x8): undefined reference to `VBlankIntrWait'
collect2: error: ld returned 1 exit status
make: *** [all] Error 1
(I have also put

Code: Select all

export PATH="/opt/devkitpro/devkitARM/bin:/opt/devkitpro/tools/bin:$PATH"
in my macOS .bashrc file)

I have devkitpro set up on my MacBook running macOS and on a dual-booted Windows 10 installation, and get the same errors on both. Am I doing something wrong?

Thanks for any help! :3

segfault
Posts: 3
Joined: Thu Mar 07, 2019 8:08 pm

Re: Yet another compiling issue thread

Post by segfault » Fri Mar 08, 2019 11:31 pm

Ah. I feel stupid, it turns out the order in which source files and library linking arguments are passed to gcc is important. :P
Putting the

Code: Select all

$(STEP1ARGS)
and

Code: Select all

$(STEP2ARGS)
variables at the end of the compilation commands (after the source files) fixed the errors I was getting.

Sorry to anyone reading this. I should have just spent some time googling it instead of posting here. :P

WinterMute
Site Admin
Posts: 1986
Joined: Tue Aug 09, 2005 3:21 am
Location: UK
Contact:

Re: Yet another compiling issue thread

Post by WinterMute » Sat Mar 09, 2019 12:50 am

segfault wrote: Thu Mar 07, 2019 8:54 pm installed devkitpro
You installed devkitARM. devkitPro is the organisation that provides the tools & libraries.

Here is the simple makefile I'm using (I'm not very good at writing makefiles, so I might have done some things wrong):
This is one of the reasons I want to redo the tonc Makefiles as standard devkitPro Makefiles. This Makefile illustrates some of the reasons why we provide a standard Makefile we encourage everyone to use rather than writing their own. You're including one of our rules files then overriding variables that are already set with values that may change in the future. You're also making up your own variable names & treating Make as if it's basically just a bat file. I'm not really sure there's any point in writing a Makefile at all if all you're going to do is pass all the source files to the compiler in one go & not deal with dependencies properly.

I appreciate that the standard Makefiles seem complicated and that Makefiles like the one you've shown here seem simple & readable but our Makefiles are designed to work mostly without user intervention and we do, of course, make every effort to ensure that they don't break with future toolchain updates. Even when the Makefiles need updating we try to ensure that end users can mostly just replace their old Makefile with the new template Makefile. They also handle dependency generation so that if header files are changed then the files which use those headers are also rebuilt.
I have devkitpro set up
Again, devkitPro is an organisation, what you have set up is devkitARM.
(I have also put
CODE: SELECT ALL

export PATH="/opt/devkitpro/devkitARM/bin:/opt/devkitpro/tools/bin:$PATH"
in my macOS .bashrc file)
Generally we recommend not adding the compiler to the global path like this since it will cause issues later if you try to use tools for other arm platforms that use the same triplet but tuned for different platforms. This is also why the standard Makefiles set the needed paths. /opt/devkitpro/tools/bin can be handy though for using some of the other tools manually from command line.

So, with the caveat that this isn't a great Makefile & you really shouldn't use it for anything serious, here are some fixes to the Makefile you posted. I've removed unnecessary variables and split things out into separate targets so it's not basically just a bat file that compiles everything every time. With this one the binary will only be rebuilt if the source changes although it will have problems with more complex projects that require headers. It's also not ideal to have everything including intermediate files in a single folder like this.

The primary source of your error was that you were specifying libraries in your link command before the object files though. You have to put libraries on the right hand side (and also order them appropriately)

Code: Select all

ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
endif

include $(DEVKITARM)/gba_rules

PROJ = demo

SRC := $(wildcard *.c)
OBJS := $(SRC:.c=.o)

INCLUDES = -I$(DEVKITPRO)/libtonc/include
LIBS = -L$(DEVKITPRO)/libtonc/lib -ltonc

CFLAGS = -mthumb-interwork -mthumb -O2 -Wall -fno-strict-aliasing $(INCLUDES)
LDFLAGS = -mthumb-interwork -mthumb -specs=gba.specs 

$(PROJ).gba: $(PROJ).elf
	$(OBJCOPY) -v -O binary $(PROJ).elf $(PROJ).gba
	gbafix $(PROJ).gba

%.o: %.c
	$(CC) $(CFLAGS) -c $< -o $@

$(PROJ).elf: $(OBJS)
	$(CC) $(LDFLAGS) $(OBJS) $(LIBS) -o $(PROJ).elf


.PHONY: clean
clean:
	rm -f $(PROJ).elf
	rm -f $(OBJS)
	rm -f $(PROJ).gba
I'll dig out a more comprehensive template later although https://github.com/devkitPro/gba-exampl ... r/template should work with minor modifications.
Help keep devkitPro toolchains free, Donate today

Personal Blog

WinterMute
Site Admin
Posts: 1986
Joined: Tue Aug 09, 2005 3:21 am
Location: UK
Contact:

Re: Yet another compiling issue thread

Post by WinterMute » Sat Mar 09, 2019 1:03 am

segfault wrote: Fri Mar 08, 2019 11:31 pm Ah. I feel stupid, it turns out the order in which source files and library linking arguments are passed to gcc is important. :P
Putting the

Code: Select all

$(STEP1ARGS)
and

Code: Select all

$(STEP2ARGS)
variables at the end of the compilation commands (after the source files) fixed the errors I was getting.

Sorry to anyone reading this. I should have just spent some time googling it instead of posting here. :P
You replied while I was still editing a more comprehensive response.

You'll notice in the edited Makefile I replaced these variables with the more standard CFLAGS & LDFLAGS which aid other people in understanding what's going on in your Makefile.

Posting here is fine, googling can and will unfortunately find many examples of people doing stuff badly and it takes a fair bit of experience to start recognising bad practices when you see them.
Help keep devkitPro toolchains free, Donate today

Personal Blog

segfault
Posts: 3
Joined: Thu Mar 07, 2019 8:08 pm

Re: Yet another compiling issue thread

Post by segfault » Sat Mar 09, 2019 4:41 am

WinterMute wrote:You installed devkitARM. devkitPro is the organisation that provides the tools & libraries.
Oh. My bad! This page of the tonc tutorial refers to devkitPro as a package and toolchain, so I guess I assumed that was what I should call the things I installed. Anyway, good to know the difference.
This Makefile illustrates some of the reasons why we provide a standard Makefile we encourage everyone to use rather than writing their own.
Fair enough. The makefile I posted was sort of a hideous combination of parts of different makefiles from tonc and the devkitPro examples as well as my own hastily-hacked-together rules just to get the one file compiled. I definitely agree that it was nowhere close to an actual makefile I'd use for serious projects. A template makefile made by people who know what they're doing would certainly help, and I'll read more on how to properly use make as well.
Generally we recommend not adding the compiler to the global path like this [...]
I see. I found this thread in which it was suggested to add those paths to the PATH variable before building, and thought it might help with my problem or be worth mentioning. I've since removed the compiler from PATH.
[...] here are some fixes to the Makefile you posted. [...] I'll dig out a more comprehensive template later although https://github.com/devkitPro/gba-exampl ... r/template should work with minor modifications.
I appreciate you taking the time to fix that messy file :P
And thanks for the link for the template. I had tried altering a copy of the gba template makefile to build my project before posting, but I couldn't get the library paths right and I gave up and made my own. I've got it working now though.

Thanks again for your help!

Post Reply

Who is online

Users browsing this forum: No registered users and 0 guests