Visão Geral


O foxmake pode ser utilizado para automatizar o processo de build de projetos C/C++, permitindo configurar tarefas e executá-las para ter como resultado final: um executável linkado, uma shared library, uma static library, execução de testes unitários, etc.

Escrevendo o FoxMakefile

O FoxMakefile é um arquivo de script e configurações. A interpretação dele é case-sensitive. Logo, a palavra "task" é reconhecido e "TASK" não.

O FoxMakefile pode ter a seguinte estrutura inicial:

output.file.name=executavel.exe

A configuração acima assume que a pasta de código fonte é a raiz do projeto e a pasta de binários também. Você pode definir as pastas como a seguir:

output.file.name=executavel.exe
src.dir=src
bin.dir=bin    
obj.dir=obj
build.dir=build
test.dir=test

Executando o foxmake

Para executar o foxmake, certifique-se de que o FoxMakefile está na raiz do seu projeto e, então, execute o seguinte comando:

foxmake compile link

Nesse comando, são especificadas duas tarefas: compile e link. Logo, o código fonte da pasta src é compilado e o executável linkado vai para pasta bin e para pasta build.

Para um primeiro exemplo com foxmake, acesse: Primeiro Exemplo

Outras opções do comando foxmake

Você pode executar o foxmake com as seguintes opções:

  • --script=scriptfile: Onde scriptfile é o nome do arquivo de script que, por padrão, é FoxMakefile.

  • --working-dir=dir: Onde dir é o diretório de trabalho base para as outras pastas e arquivos. As pastas bin, src, etc. Se não absolutas, são relativas ao diretório de trabalho que, por padrão é o diretório onde o arquivo de script foi encontrado.

  • --verbose e -v: Uma dessas opções pode habilitar, se informada, uma impressão detalhada de informações da execução das tarefas na saída padrão.

  • --no-resume: Essa opção pode desabilitar a impressão de um resumo da execução das tarefas na saída padrão.

  • -prop name=value: Onde name é o nome da propriedade e value é o valor.

  • -var name=value: Onde name é o nome da variável e value é o valor.

Exemplos:

foxmake build --working-dir="C:\Meus Projetos\projeto" --script=FoxMakefile2
foxmake build -prop output.file.name=helloworld.exe -var target=release

A opção prop seta uma propriedade que poderia ser definida no arquivo de script (O FoxMakefile).

Veja mais alguns exemplos:

foxmake buildall -v

No exemplo acima, uma saída detalhada sobre a execução das tarefas é impressa na saída padrão.

foxmake buildall --no-resume

No exemplo acima, nem são impressos detalhes da execução das tarefas nem o resumo.

Comentários

Para criar comentários, basta iniciar a linha a ser comentada com #. Para comentar várias linhas, basta iniciar o bloco de comentários com ## e finalizar com ## conforme o exemplo a seguir:

# Comentário

##
Bloco de comentários
Aqui pode haver várias linhas 
de comentário
##

Tarefas

O foxmake suporta as seguintes tarefas:

  • clean
  • compile
  • compileall
  • link
  • archive
  • test
  • copy
  • build
  • archivebuild
  • testbuild
  • buildall
  • archivebuildall
  • testbuildall

A tarefa clean

A tarefa clean exclui os arquivos criados na execução das tarefas de compile, compileall, link, archive, copy.


A tarefa compile

A tarefa compile compila todos os arquivos de código fonte de implementação encontrados na pasta correspondente a propriedade "src.dir" e na pasta correspondente a propriedade "test.dir" (caso seja definida). O resultado da compilação é os arquivos objeto que são colocados na pasta correspondente a propriedade "obj.dir".


A tarefa compileall

Semelhante a tarefa compile. Com a diferença de que sempre compila todos os arquivos de implementação. A tarefa compile, compila de forma inteligente, levando em conta se o arquivo foi alterado desde a última compilação para decidir se é necessário compilá-lo novamente.



A tarefa archive

A tarefa archive pode ser utilizada para empacotar uma biblioteca estática (static library).


A tarefa test

A tarefa test pode ser utilizada para executar testes unitários criados com uso do framework xutest. Para tanto, é necessário configurar a pasta de testes e a lib do xutest deve ser incluída no comando de linker.


A tarefa copy

Esta tarefa, quando executada, copia os arquivos resultantes da compilação, linkagem e empacotamento para pasta de build.


Tarefas de build

As tarefas de build combinam a execução das tarefas conforme a seguir:

  • build: clean, compile, link, copy
  • archivebuild: clean, compile, archive, copy
  • testbuild: clean, compile, link, copy, test
  • buildall: clean, compileall, link, copy
  • archivebuildall: clean, compileall, archive, copy
  • testbuildall: clean, compileall, link, copy, test

Propriedades

As definições de propriedades são interpretadas em tempo de interpretação (com a interpretação do script).

O foxmake tem as seguintes propriedades que podem ser definidas no arquivo de script - FoxMakefile:

  • compiler
  • archiver

  • base.dir
  • src.dir
  • bin.dir
  • obj.dir
  • build.dir
  • test.dir

  • compiler.params
  • linker.params
  • archiver.params
  • include.dirs
  • lib.dirs
  • libs
  • defines

  • test.compiler.params
  • test.linker.params
  • test.include.dirs
  • test.lib.dirs
  • test.libs
  • test.defines

  • output.file.name
  • resource.file

  • output.def.file
  • out.implib.file

As propriedades: compiler, archiver especificam os programas de linha de comando que serão utilizados para essas operações: Exemplo:

compiler=g++
archiver=ar

Os diretórios de arquivos podem ser organizados conforme o exemplo a seguir:

base.dir=.
src.dir=src
bin.dir=bin
obj.dir=obj
build.dir=build
test.dir=test

As propriedades: compiler.params, linker.params e archiver.params podem ser utilizadas para passar parâmetros/opções extras aos comandos correspondentes. Exemplo:

compiler.params=-Wall -g
linker.params=-s -static

Esse exemplo executa os seguintes comandos para a compilação e linkagem de um simples arquivo main.cpp

g++ -Wall -g -c main.cpp -o main.o
g++ -o main.exe main.o -s -static

As propriedades: include.dirs, lib.dirs, libs, podem ser configuradas como a seguir:

include.dirs=include
lib.dirs=lib
libs=xutest SDL2

Esse exemplo inclue no compilador a opção: -Iinclude, e ao linker as opções: -Llib -lxutest -lSDL2

A propriedade defines pode ser utilizada para incluir definições ao compilador. Como se estivessem sido definidas com #define. Exemplo:

defines=BUILDDLL

No exemplo acima, é incluída a seguinte opção ao compilador: -DBUILDDLL

As propriedades que se iniciam com teste são semelhantes as de mesmo nome que não se iniciam com prefixo test. A diferença é que são aplicadas a compilação e linkagem do executável de testes, não a compilação e linkagem do executável do projeto.

A propriedade output.file.name pode ser utilizada para configurar o nome do arquivo de saída que pode ser um executável, uma shared library ou uma static library. Exemplo:

output.file.name=executavel

A propriedade resource.file funciona no windows para que um arquivo de ícone seja colocado no executável. Exemplo:

resource.file=icone.res

As propriedades output.def.file e out.implib.file podem ser definidas para geração de shared libraries. Exemplo:

output.def.file=xutest.def
out.implib.file=libxutest.a

Com essa configuração, são adicionadas as seguintes linhas ao linker: -Wl,--output-def=xutest.def -Wl,--out-implib=libxutest.a

Chamando propriedades

Para chamar uma propriedade, você pode usar a sintaxe: $(nomedapropriedade). Exemplo:

echo $(src.dir)

O comando acima exibe o valor da propriedade "src.dir"

Utilizando $ como caractere

Se precisar utilizar o caractere $ como texto nas definições de propriedades ou variáveis, é necessário utilizar \$. o contra barra diz ao interpretador que o \$ é um caractere e não o início da chamada de uma variável ou propriedade. Exemplo:

linker.options=\$(sdl2-config --cflags --libs)

O exemplo acima funciona no linux e executa "sdl2-config --cflags --libs" no shell do linux durante a execução da linkagem.

Variáveis

É possível também definir variáveis tipo texto e usá-las em comandos, valores de propriedades, etc.

Variáveis devem ser sempre definidas iniciando com um $ e chamadas com $(nomedavariavel).

Variáveis são interpretadas em tempo de interpretação (com a interpretação do script) se estiverem definidas fora de algum bloco de definição de task ou procedure. Caso contrário, se estiver no bloco de alguma definição de task ou procedure, é executada em tempo de execução, com a execução do bloco.

O foxmake carrega algumas variáveis predefinidas antes do script ser interpretado. São elas:

  • working_dir: Assume o valor do diretório de trabalho.
  • script_file: Assume o valor do caminho do script carregado.
  • os: Assume um valor correspondente ao sistema operacional. Isto é, se é windows ou não.
    A variável "os" pode assumir dois valores: "windows" ou "nowindows".

Vamos a um exemplo:

$release_dir=Release
bin.dir=bin/$(release_dir)

No exemplo acima, "$(release_dir)" é substituído por "Release" e "bin.dir" passa a valer: "bin/Release". Vamos a outro exemplo:

$target=release
$debug_bin_dir=bin/Debug
$release_bin_dir=bin/Release

bin.dir=bin/$($(target)_bin_dir)

No exemplo acima, $(target) é substituído por "release" e "bin.dir" passa a valer "bin/Release". Se substituir o valor da variável $target por "debug", sem aspas, "bin.dir" passa a valer "bin/Debug"

Agora veja outro exemplo utilizando variáveis predefinidas:

echo $working_dir
echo $script_file
echo $os

O exemplo acima imprime os valores das variáveis predefinidas.

Atenção: Não é permitido alterar valor de variáveis predefinidas. Logo, é inválido fazer conforme a seguir:

$os=linux

Definição de blocos de tarefa default

É possível configurar no script um bloco de código que pode ser executado antes ou depois da execução de uma tarefa. Exemplo:

task compile before
    echo Iniciando compilação...
endtask

task compile after
    echo Finalizando compilação...
endtask

Nesse exemplo, antes da task compile ser executada, é impressa na saída padrão a mensagem "Iniciando compilação..." e, depois da tarefa ser executada, é impressa a mensagem "Finalizando compilação...".


Executando antes ou após tudo

É possível definir as tarefas init e finish para executar o bloco de código no início ou no final do processo de build. Exemplo:

task init before
    echo Executando isto antes de tudo...
endtask

task finish after
    echo Executando isto após tudo...
endtask

Tarefas de usuário

Você pode definir sua própria tarefa como no exemplo a seguir:

task minhatask
    echo Executando minha task...
endtask

Para executar a task acima, basta executar o seguinte comando:

foxmake minhatask

Ela também pode ser combinada com as demais tasks, exemplo:

foxmake compile minhatask

Configuração de tarefas

Você pode configurar tarefas especificando as flags na definição de sua configuração.

São suportadas as seguintes flags

  • verbose
  • noverbose
  • showoutput
  • noshowoutput

A definição de configuração de tarefa deve finalizar com ponto e virgula e apenas tarefas default podem ser configuradas assim.

Veja os exemplos abaixo:

task compile verbose;

No exemplo acima é definido que todos os detalhes da task compile devem ser impressos na saída padrão independente do foxmake está configurado como não verbose.

task compile noverbose;

No exemplo acima é definido que os detalhes da task compile não devem ser impressos na saída padrão independente do foxmake está configurado como verbose.

task compile showoutput;

No exemplo acima define que a saída dos comandos da tarefa compile deve ser mostrada na saída padrão.

task compile noshowoutput;

No exemplo acima define que a saída dos comandos da tarefa compile não deve ser mostrada na saída padrão.

Você pode combinar flags como no exemplo abaixo:

task compile verbose noshowoutput;

Procedimentos

Para criar um procedimento, utilize a palavras reservadas "proc" e "endproc". Para chamar o procedimento, utilize "call". Veja o exemplo abaixo:

proc imprimeMensagemDeCompile
    echo A compilação está sendo executada...
endproc

task compile before
    call imprimeMensagemDeCompile
endtask

task compileall before
    call imprimeMensagemDeCompile
endtask

No exemplo acima está sendo definido um procedimento de nome "imprimeMensagemDeCompile" e ele está sendo chamado dentro das definições de tasks: "task compile before" e "task compileall before"

Comandos

São suportados os seguintes comandos:

  • cd
  • cp
  • rm
  • mkdir
  • echo

O comando cd

O comando CD pode ser utilizado para mudar o diretório corrente. Exemplo:

cd ../build

O comando cp

O comando CP pode ser utilizado para copiar arquivos e diretórios de um lugar para outro. Exemplos:

cp $(src.dir)/* source/

O exemplo acima copia todo o conteúdo da pasta referente a propriedade "src.dir" para a pasta "source".

cp -r $(src.dir)/*.h source/

O exemplo acima copia todos os arquivos com extensão .h contidos na pasta de "src" para pasta "source", mantendo a estrutura de diretórios da pasta de origem "src". O "-r" significa recursivo.

cp -r proj/src/**/math/*.h source/

O exemplo acima copia todos os arquivos com extensão .h contidos na pasta "proj/src" para pasta "source", mantendo a estrutura de diretórios após o src. Exemplo, o arquivo "proj/src/math/trigon/trigon.h" é copiado como alvo: "source/math/trigon/trigon.h

cp main.cpp source/main.cpp

Copia main.cpp para pasta source.

cp main.cpp source/

Copia main.cpp para pasta source.

cp main.cpp source/main2.cpp

Copia main.cpp para pasta source com o nome de cópia "main2.cpp".

cp -r src source

Se a pasta source existir, é copiada a pasta src para dentro dela. Caso contrário, a pasta src é copiada com nome "source". Exemplo, se "source" existir, o alvo é: "source/src". Se 'source" não existir, o alvo é: "source"

cp -r so*es.* sources/

O comando acima copia todos os arquivos da pasta corrente e derivadas cujo nome iniciar com "so" e finalizar com "es" e tiver qualquer extensão.

cp lib*.a sources/

O comando acima copia todos os arquivos da pasta atual (sem recursividade) que começarem com "lib" e tiverem extensão ".a", para pasta "sources".


O comando rm

O comando rm pode ser utilizado para remover arquivos e diretórios conforme os exemplos a seguir:

rm main.cpp

O comando acima remove o arquivo main.cpp

rm *

O comando acima remove todos os arquivos do diretório atual (sem recursividade). Logo, se houverem pastas não vazias, o comando retorna uma mensagem sobre não ter sido possível completar a remoção.

rm -r *

O comando acima remove todos os arquivos do diretório atual (recursivamente), incluindo pastas não vazias.

rm -r lib*

O comando acima remove todos os arquivos e diretórios do diretório atual (recursivamente), cujo nome começar com "lib".


O comando mkdir

O comando mkdir cria uma nova pasta ou todas as pastas do caminho se "-p" for informado como opção. Exemplos:

mkdir pasta

O comando acima cria a pasta "pasta" no diretório corrente.

mkdir -p /home/joao/projetos/projeto-cpp

O comando acima cria todos os subdiretórios, caso já não existam. Supondo que a pasta "projetos" não existe em "/home/joão", ela é criada e é criada dentro dela a pasta "projeto-cpp".


O comando echo

O comando echo exibe o texto passado como argumento para ele. Exemplos:

echo Hello World!
echo O diretório src tem o caminho: $(src.dir)

Coringas

Os coringas nos comandos de arquivos servem para incluir na operação arquivos, dado um filtro pelo nome do arquivo ou para substituir parte do caminho no caso do duplo coringa. Veja os exemplos abaixo:

rm -r dir/*

O exemplo acima remove todos os arquivos e pastas do diretório "dir"

rm -r dir/*.*

O exemplo acima remove todos os arquivos com qualquer extensão, excerto as pastas e arquivos sem extensão, do diretório "dir"

rm -r dir/lib*

O exemplo acima remove todos os arquivo e pastas do diretório "dir" que começarem com o prefixo "lib".

rm -r dir/*lib

O exemplo acima remove todos os arquivos e pastas cujo nome (não a extensão) terminar com "lib"

rm -r dir/*lib*

O comando acima remove todos os arquivos e pastas cujo nome começar com "lib", terminar com "lib" ou tiver "lib" no meio.

rm -r dir/*lib*.*

O comando acima remove todos os arquivos com qualquer extensão e cujo nome começar com "lib", terminar com "lib" ou tiver "lib" no meio. Não remove pastas nem arquivos sem extensão.

rm -r dir/lib*.txt

O comando acima remove todos os arquivos com extensão .txt que começarem com "lib" e estiverem dentro do diretório "dir".

Atenção: o foxmake ainda não suporta multiplas origens como no linux. Por exemplo, o seguinte exemplo não é válido:

rm a.txt b.bat pasta

Executando comandos do shell

Para executar comandos do shell do sistema operacional, basta informar o @ antes do comando ou colocar o comando dentro do bloco de código de shell. Exemplos:

@ls

O comando acima executa o comando ls se estiver no linux.

Bloco de código shell

Para criar um bloco de código do shell, você pode fazer conforme o exemplo abaixo:

shellbegin
    echo "Verificando se o arquivo FoxMakefile existe..."
    if [ -f "FoxMakefile" ]; then
        echo "O arquivo FoxMakefile existe...!"
    else 
        echo "Arquivo nao existe: FoxMakefile"
    fi
endshell

O código acima executa o conteúdo entre o "shellbegin" e o "endshell" como se estivesse em um arquivo de shell do linux. Isso caso esteja no linux. Funciona para windows se modificar a sintaxe do comando acima para a sintaxe de arquivos batch do windows. Exemplo:

shellbegin
    echo Verificando se o arquivo FoxMakefile existe...
    if exist FoxMakefile (
        echo O arquivo FoxMakefile existe!
    ) else (
        echo O arquivo FoxMakefile nao existe.
    )
endshell

Diretiva IF

Você utilizar a diretiva IF para interpretar algo, apenas se uma condição for satisfeita. Exemplo:

IF os == "windows"
    test.linker.params=-ldl -rdynamic
ENDIF

No exemplo acima, a propriedade "test.linker.params" só é definida se estiver no windows. Isto é, o valor da variável "os" for "windows".

A diretiva IF é processada em tempo de interpretação. Diferente da instrução if que é executada em tempo de execução.

Instrução IF

A instrução IF pode ser utilizada para executar algo apenas se uma condição for verdadeira, ou caso contrário. A instrução if é executada em tempo de execução. Logo, deve ser colocada dentro de algum bloco de código de definição de task ou de procedure ou estar no final do script. Dado que se estiver fora do escopo das definições de tasks e procedures, só é executado após todas as tarefas serem executadas.

A condição do if pode envolver valores string (entre aspas), variáveis ou propriedades e operador de comparação "==" para igualdade e "!=" para diferença. Veja o exemplo abaixo

task test before
    if test.dir == "" then
        echo Propriedade test.dir não definida.
    else if test.dir == "test" then
        echo Propriedade test.dir aponta para pasta test/
        if src.dir == "src"
            echo A propriedade src.dir aponta para src/
        endif
    else
        echo A propriedade test.dir tem o valor: $(test.dir)
    endif
endtask

O exemplo acima testa os valores das propriedades: "test.dir" e "src.dir" e imprime uma ou duas mensagens, dependendo do valor delas.

Veja outro exemplo:

proc printDirContent
    if os == "windows" then
        @dir
    else 
        @ls
    endif
endtask

A procedure acima executa o comando dir, se estiver no windows e ls, se estiver em outro sistema operacionsl como, por exemplo, o linux.

Tempo de execução e tempo de interpretação

As propriedades e variáveis definidas fora do escopo de definição de tasks e procedures são executadas (interpretadas) durante a interpretação o script. Já tudo que está dentro das procedures e definições de tasks (incluíndo atribuições a variáveis) é executado em tempo de execução. Isto é, durante a execução do bloco de definição de task ou procedure.

Para utilizar ifs fora dos blocos de definição de tarefas e procedures, é necessário utilizar a diretiva if que deve ser utilizada em maiuscula: IF, ENDIF. Ifs (em minusculo) são instruções executadas em tempo de execução e o que está fora dos blocos de definição de tarefas e procedures é executado em tempo de interpretação.

Veja os exemplos abaixo:

IF os != "windows"
    test.linker.params=-ldl -rdynamic
ENDIF

O código acima executa (em tempo de interpretação) uma diretiva IF que verifica se o sistema operacional é diferente de "windows" e, se for, seta o valor da propriedade "test.linker.params". Agora veja o exemplo abaixo:

if os != "windows" then
    test.linker.params=-ldl -rdynamic
endif

O exemplo acima gera um erro. Isso porque as propriedades são executadas (interpretadas e carregadas) em tempo de interpretação e o if é executado em tempo de execução. Isto é, o if, neste caso, só é executado após todas as tarefas, onde, os comandos definidos no script fora de qualquer bloco de definição de tarefas ou procedure são executados. Para o exemplo acima funcionar, é necessário utilizar diretivas IF ao invés de comandos if.

Testes com xutest

Atenção:
Para visitar a página do framework xutest, acesse: xutest.

Para executar testes com xutest você pode utilizar as seguintes tarefas: test, testbuild ou testbuildall

Para tanto, é necessário definir as propriedades "src.dir", "test.dir" e "test.libs".

Se estiver no linux, é necessário também definir a propriedade "test.linker.params"

Caso não tenha instalado o xutest e quiser incluí-lo manualmente no projeto, precisa definir também as propriedades "test.include.dirs", "test.lib.dirs".

Se o xutest já estiver instalado no sistema operacional, não precisa definir as propriedades "test.include.dirs" e "test.lib.dirs".

Atenção: A propriedade "src.dir" deve ser definida e ter valor diferente do diretório raiz do projeto ou parente dele.

Após configurado o FoxMakefile com os caminhos dos includes, lib do xutest e parâmetros para o comando que linka os arquivos de teste, é só criar os testes na pasta da propriedade "test.dir".

Veja abaixo o exemplo de configuração básica do script FoxMakefile:

src.dir=src
test.dir=test
test.libs=xutest

IF os != "windows"
    # interpretado somente se estiver no linux
    test.linker.params=-ldl -rdynamic
ENDIF

O exemplo acima assume que a pasta de testes é a pasta "test".

Caso não tenha instalado o xutest e quiser inclur manualmente, faça conforme o seguinte exemplo:

src.dir=src
test.dir=test

test.include.dirs=include
test.lib.dirs=.
test.libs=xutest

IF os != "windows"
    # interpretado somente se estiver no linux
    test.linker.params=-ldl -rdynamic
ENDIF

O exemplo acima assume que o diretório onde deve procurar o arquivo "libxutest.a" é o diretório corrente. Para outro diretório, é só alterar "test.lib.dirs". Claro, o exemplo acima assume que o nome da static library do xutest tem o nome "libxutest.*", onde o "*" geralmente corresponde a "a" ou "lib". Esse exemplo assume também que a os arquivos de cabeçalho do xutest estão na pasta "include".

Veja um exemplo de teste, supondo a criação do arquivo "test/main.cpp"

#include <xutest/xutest.h>

TEST_CASE( primeiroTeste, Testes ) {
    int a = 1;
    int b = 1;
    int c = 2;
    ASSERT_EQUALS( a, b, )
    ASSERT_TRUE( a == b, )
    ASSERT_FALSE( a == c, )
}

int main() {
    RUN_TEST_CASES_MENU();
    return 0;
}

Você pode executar o seguinte comando, após configurar as propriedades no FoxMakefile e criar o arquivo "test/main.cpp" com o conteúdo acima:

foxmake testbuild

A saída do comando deve ser a seguinte:

Criando uma static library

Uma static library pode ter seu código objeto incluído diretamente no código objeto do seu arquivo executável linkado com a static library. Diferente das dlls, cujo código objeto é carregado dinamicamente.

Para criar uma static library, deve ser utilizado o archiver, ao invés do compilador gcc/g++. Você pode configurar o FoxMakefile conforme a seguir:

output.file.name=libxutest.a

O CBuidFile acima especifica o arquivo de saída que, no caso de static libraries, deve começar com "lib" e ter extensão ".a" ou, no windows, pode ter extensão ".lib" ou ".a".

Para gerar a static library, execute o seguinte comando:

foxmake archivebuildall

Esse comando compila todos os arquivos e empacota numa static library.

Para um exemplo prático e completo, visite: exemplo completo

Criando uma dynamic library

Uma dynamic library pode ser carregada no momento da execução do executável, isto é, dinamicamente. Diferente das static libraries, cujo código objeto é adicionado ao executável no momento da linkagem.


Dynamic Library para windows

Para gerar uma biblioteca dinâmica ou compartilhada (shared library) no windows, você pode fazer conforme a seguir no FoxMakefile:

output.file.name=libestruturas.dll

defines=BUILDING_DLL

O exemplo acima gera uma biblioteca dinâmica com extensão .dll para windows. Isto é, a biblioteca tem o nome libestruturas.dll e, essa dll necessita da definição de uma constante macro de nome BUILDING_DLL. Isso porque no código fonte tem algo como:

#ifdef BUILDING_DLL
    #define ESTRUTURA_API __declspec(dllexport)
#else
    #define ESTRUTURA_API __declspec(dllimport)
#endif

Para gerar a biblioteca dinâmica, basta executar o seguinte comando:

foxmake buildall

Com o comando acima, o foxmake entende que deve gerar uma dynamic library tipo .dll, ao invés de um executável, por causa da extensão do arquivo de saída "libestruturas.dll".


Dynamic Library para linux

Para gerar uma biblioteca dinâmica para linux, você pode fazer o FoxMakefile conforme a seguir:

output.file.name=libestruturas.so

compiler.params=-fPIC

No linux é necessário passar o parâmetro -fPIC para o compilador para criar uma shared library .so.

Para gerar a shared library (dynamic library) basta executar o comando a seguir:

foxmake buildall

O foxmake entende que deve gerar uma biblioteca dinâmica, ao invés de um executável, pela extensão do .so do arquivo "libestruturas.so".

Para um exemplo prático e completo, visite: exemplo completo

Finalizando...

E assim chegamos ao final da visão geral sobre o foxmake.

Para exemplos de uso, acesse: Get Started.