O GNU make é frequentemente usado como um componente dentro de um conjunto maior de ferramentas, como ambientes de desenvolvimento integrado (IDEs) e cadeias de ferramentas (toolchains) de compiladores. O papel do make é iniciar comandos e determinar se eles foram bem-sucedidos ou não. Para cumprir apenas esse papel, não é necessário nenhum mecanismo especial de integração. No entanto, às vezes é conveniente vincular o make de forma mais estreita às outras partes do sistema. Isso inclui tanto a integração com componentes de nível superior (ferramentas que invocam o make) quanto com componentes de nível inferior (ferramentas que o make invoca).
O GNU make tem a capacidade de executar várias receitas (recipes) em paralelo (consulte Execução paralela) e ainda de impor um limite ao número total de tarefas (jobs) paralelas mesmo entre invocações recursivas do make (consulte Comunicando opções a um sub-make). Algumas ferramentas que o make invoca também são capazes de executar várias operações em paralelo, usando múltiplas threads ou múltiplos processos. Essas ferramentas podem ser aprimoradas para participar do mecanismo de gerenciamento de tarefas do GNU make, de modo que o número total de threads/processos ativos em execução no sistema não exceda o número máximo de slots fornecido ao GNU make.
O GNU make usa um método chamado "jobserver" para controlar o número de tarefas (jobs) ativas entre invocações recursivas. A implementação concreta do jobserver varia conforme o sistema operacional, mas alguns aspectos fundamentais são sempre comuns.
Primeiro, o make passa aos processos filhos, por meio da variável de ambiente MAKEFLAGS, as informações necessárias para acessar o jobserver. As ferramentas que desejam participar do protocolo do jobserver precisam analisar essa variável de ambiente e encontrar a palavra que começa com --jobserver-auth=. O valor dessa opção descreve como se comunicar com o jobserver. A interpretação desse valor é descrita nas seções a seguir.
Observe que a variável MAKEFLAGS pode conter várias instâncias da opção --jobserver-auth=. Apenas a última instância é relevante.
Segundo, todo comando que o make inicia tem, antes de iniciar, um slot de tarefa implícito já reservado para ele. Qualquer ferramenta que deseje participar do protocolo do jobserver pode supor que sempre é capaz de executar uma tarefa sem precisar entrar em contato com o jobserver.
Por fim, é crucial que as ferramentas que participam do protocolo do jobserver devolvam ao jobserver o número exato de slots que dele obtiveram antes de terminar — mesmo em condições de erro. Lembre-se de que o slot de tarefa implícito mencionado antes não deve ser devolvido ao jobserver! Devolver poucos slots significa que esses slots ficarão perdidos durante todo o restante do processo de build; devolver slots demais significa que slots extras ficarão disponíveis. O comando make de nível superior exibe uma mensagem de erro ao final do build se detectar um número incorreto de slots disponíveis no jobserver.
Como exemplo, suponha que você esteja implementando um linker (ligador) com suporte a operação multithread. Você gostaria de aprimorar o linker para que, quando invocado pelo GNU make, ele participe do protocolo do jobserver a fim de controlar quantas threads são usadas durante a ligação. Primeiro, será preciso modificar o linker para determinar se a variável de ambiente MAKEFLAGS está definida. Em seguida, será preciso analisar o valor dessa variável para determinar se o jobserver está disponível e como acessá-lo. Se estiver disponível, você pode acessá-lo para obter slots de tarefa, controlando quanto paralelismo sua ferramenta pode usar. Quando terminar, sua ferramenta deve devolver esses slots de tarefa ao jobserver.
Em sistemas POSIX, o jobserver é implementado de uma de duas maneiras. Em sistemas que oferecem suporte a isso, o GNU make cria um named pipe (pipe nomeado) e o utiliza como jobserver. Nesse caso, a opção auth tem a forma --jobserver-auth=fifo:PATH, em que "PATH" é o nome de caminho do named pipe. Para acessar o jobserver, você deve abrir o caminho do named pipe e ler/escrever nele conforme descrito a seguir.
Se o sistema não oferecer suporte a named pipes, ou se o usuário fornecer a opção --jobserver-style e especificar "pipe", o jobserver será implementado como um simples pipe UNIX. Nesse caso, a opção auth tem a forma --jobserver-auth=R,W, em que "R" e "W" são inteiros não negativos que representam descritores de arquivo: "R" é o descritor de arquivo de leitura e "W" é o descritor de arquivo de escrita. Se um desses descritores de arquivo, ou ambos, for negativo, isso significa que o jobserver está desabilitado para este processo.
Ao usar um pipe simples, somente as linhas de comando que o make reconhece como invocações recursivas do make (consulte Como funciona a variável MAKE) terão acesso ao jobserver. Ao escrever makefiles, você deve se certificar de marcar o comando como recursivo (a forma mais comum é prefixar a linha de comando com o indicador +; consulte Uso recursivo do make). Observe que o lado de leitura do pipe do jobserver está configurado no modo "blocking" (bloqueante). Isso não deve ser alterado.
Em ambas as implementações do jobserver, o pipe é pré-carregado com um token de um único caractere para cada tarefa disponível. Para obter um slot adicional, você deve ler um único caractere do jobserver; para liberar um slot, você deve escrever um único caractere de volta no jobserver.
É importante que, ao liberar o slot de tarefa, você escreva de volta o mesmo caractere que leu. Não presuma que todos os tokens sejam o mesmo caractere; caracteres diferentes podem ter significados diferentes para o GNU make. A ordem não importa, pois o make não tem ideia de em que ordem as tarefas serão concluídas.
Há várias condições de erro que você deve considerar para garantir que sua implementação seja robusta:
--jobserver-auth, ela deve supor que o jobserver está usando um estilo diferente e que não é possível se conectar.
--jobserver-auth aponta para um pipe simples, mas que os descritores de arquivo especificados estão fechados, isso significa que o processo make que a chamou não considerou sua ferramenta uma invocação recursiva do make (por exemplo, a linha de comando não foi prefixada com o caractere +). Você deve notificar seus usuários sobre essa situação.
SIGINT) etc. Você pode querer instalar manipuladores de sinal (signal handlers) para gerenciar essa devolução.
MAKEFLAGS e procurar o caractere n. Se esse caractere estiver presente, o make foi invocado com a opção "-n", e talvez sua ferramenta deva parar sem realizar nenhuma operação.
Em sistemas Windows, o jobserver é implementado como um semáforo nomeado (named semaphore). O semáforo é configurado com uma contagem inicial igual ao número de slots disponíveis. Para obter um slot, você deve aguardar (wait) no semáforo (com ou sem timeout). Para liberar um slot, libere (release) o semáforo.
Para acessar o semáforo, você deve analisar a variável MAKEFLAGS e procurar a string de argumento --jobserver-auth=NAME, em que "NAME" é o nome do semáforo nomeado. Passe esse nome para OpenSemaphore a fim de criar um handle para o semáforo.
O único estilo válido para --jobserver-style é "sem".
Há várias condições de erro que você deve considerar para garantir que sua implementação seja robusta:
SIGINT) etc. Você pode querer instalar manipuladores de sinal (signal handlers) para gerenciar essa devolução.
Normalmente, o GNU make invoca todos os comandos com acesso à mesma saída padrão e saída de erro padrão com as quais o próprio make foi iniciado. Muitas ferramentas detectam se a saída vai para um terminal ou não e usam essa informação para alterar o estilo de saída. Por exemplo, se a saída vai para um terminal, a ferramenta pode adicionar caracteres de controle que definem cor ou até alterar a posição do cursor. Se a saída não vai para um terminal, esses caracteres de controle especiais não são emitidos, para que não corrompam arquivos de log etc.
A opção --output-sync (consulte Saída durante a execução paralela) anula essa detecção de terminal. Quando a sincronização de saída está habilitada, o GNU make faz com que toda a saída de comando seja escrita primeiro em um arquivo, para que essa saída possa ser escrita como um bloco único, sem interferência da saída de outros comandos. Como resultado, todas as ferramentas invocadas pelo make acreditam que sua saída não será exibida em um terminal, mesmo quando ela será (porque o make a exibirá lá após a conclusão do comando).
Para auxiliar as ferramentas que gostariam de determinar se sua saída será ou não exibida em um terminal, o GNU make define as variáveis de ambiente MAKE_TERMOUT e MAKE_TERMERR antes de invocar qualquer comando. As ferramentas que desejam determinar se a saída padrão (ou a saída de erro, respectivamente) será exibida em um terminal podem verificar se essas variáveis de ambiente existem e contêm um valor não vazio. Em caso afirmativo, a ferramenta pode supor que a saída será (em algum momento) exibida em um terminal. Se as variáveis não estiverem definidas ou tiverem um valor vazio, a ferramenta deve recorrer aos seus métodos habituais para detectar se a saída vai ou não para um terminal.
O conteúdo dessas variáveis pode ser analisado para determinar o tipo de terminal que será usado para exibir a saída.
De maneira análoga, os ambientes que invocam o make e gostariam de capturar a saída para, ao final, exibi-la em um terminal (ou em algum dispositivo de exibição capaz de interpretar caracteres de controle de terminal) podem definir essas variáveis antes de invocar o make. O GNU make não modifica essas variáveis de ambiente se elas já existirem quando ele é iniciado.