Quais fatores sociais ou técnicos levaram ao surgimento da mentalidade do CIY?
A causa raiz é obviamente a razão técnica: a portabilidade binária é mais difícil que a portabilidade de origem . Fora dos pacotes de distribuição, a maioria dos softwares livres ainda está disponível apenas na forma de código-fonte, porque é muito mais conveniente para o (s) autor (es) / mantenedor (es).
Até as distribuições Linux começarem a empacotar a maioria das coisas que as pessoas comuns gostariam de usar, sua única opção era obter a fonte e compilá-la para seu próprio sistema. Os fornecedores comerciais do Unix geralmente não incluíam coisas que quase todo mundo queria (por exemplo, um bom shell como o GNU bash
ou similar), apenas sua própria implementação sh
e / ou csh
, então você precisava criar as coisas sozinho, se quisesse (como administrador de sistemas) para fornecer um ambiente Unix agradável aos seus usuários para uso interativo.
A situação agora, com a maioria das pessoas sendo o único administrador e o único usuário da máquina em sua área de trabalho, é muito diferente do modelo tradicional do Unix. Um administrador de sistema mantinha o software no sistema central e na área de trabalho de todos. (Muitas vezes, tendo as estações de trabalho das pessoas apenas montadas em NFS /opt
e a /usr/local/
partir do servidor central e instalando coisas lá.)
Antes de coisas como .NET e Java, a verdadeira portabilidade binária em diferentes arquiteturas de CPU era impossível. A cultura Unix evoluiu com portabilidade de origem como padrão por esse motivo, com pouco esforço para tentar habilitar a portabilidade binária até esforços recentes do Linux como o LSB. Por exemplo, o POSIX ( o principal padrão Unix) tenta apenas padronizar a portabilidade de origem, mesmo em versões recentes.
Fator cultural relacionado: Os primeiros comerciais da AT&T Unix vieram com código-fonte (em fitas). Você não precisava criar o sistema a partir da fonte, ele estava lá para o caso de você querer ver como algo realmente funcionava quando os documentos não eram suficientes.
A Wikipedia diz :
"A política do Unix de extensa documentação on-line e (por muitos anos) acesso pronto a todo o código-fonte do sistema aumentou as expectativas dos programadores e contribuiu para o lançamento do movimento de software livre em 1983".
Não sei ao certo o que motivou essa decisão, já que é inédito nos dias de hoje oferecer aos clientes o código-fonte do software comercial. Existem claramente alguns preconceitos culturais nessa direção, mas talvez isso tenha surgido das raízes do Unix como um sistema operacional portátil escrito principalmente em C (não na linguagem assembly) que poderia ser compilado para diferentes hardwares. Eu acho que muitos sistemas operacionais anteriores tinham mais de seu código escrito em asm para uma CPU específica, portanto a portabilidade no nível da fonte foi um dos pontos fortes do Unix. (Eu posso estar errado sobre isso; não sou especialista no Unix inicial, mas Unix e C estão relacionados.)
A distribuição de software na forma de origem é, de longe, a maneira mais fácil de permitir que as pessoas o adaptem a qualquer sistema em que desejam que ele seja executado. (Usuários finais ou pessoas que o empacotam para uma distribuição Linux). Se o software já foi empacotado por / para uma distribuição, os usuários finais podem apenas usá-lo.
Mas é demais esperar que os autores da maioria dos pacotes façam binários para cada sistema possível. Alguns projetos importantes fornecem binários para alguns casos comuns (especialmente x86 / windows em que o sistema operacional não vem com um ambiente de construção e o fornecedor do sistema operacional enfatiza bastante a distribuição de instaladores apenas binários).
Conseguir que um software seja executado em um sistema diferente daquele usado pelo autor pode exigir algumas pequenas alterações, que são fáceis com a fonte . Um pequeno programa único que alguém escreveu para coçar sua própria coceira provavelmente nunca foi testado na maioria dos sistemas obscuros. Ter a fonte torna possível fazer essas alterações. O autor original pode ter esquecido algo ou intencionalmente escreveu um código menos portátil, porque economizou muito tempo. Mesmo grandes pacotes como o Info-ZIP não tinham testadores em todas as plataformas imediatamente e precisavam de pessoas para enviar seus patches de portabilidade à medida que os problemas fossem descobertos.
(Existem outros tipos de problemas de portabilidade de nível de fonte que só acontecem por causa de diferenças na construção env, e não são realmente relevantes para a questão aqui. Com Java-style portabilidade binária, auto-ferramentas ( autoconf
/ auto-make
) e coisas semelhantes, como cmake
wouldn E não teríamos coisas que alguns sistemas exigem a inclusão de, em <netinet/in.h>
vez de<arpa/inet.h>
parantohl(3)
(e talvez não tivéssemos ntohl()
ou qualquer outra coisa de ordem de bytes em primeiro lugar!)
Eu desenvolvo em linguagens .NET regularmente, por isso não sou analfabeto do computador.
Compilar uma vez, executar em qualquer lugar é um dos principais objetivos do .NET e também do Java; portanto, é justo dizer que linguagens inteiras foram inventadas em um esforço para resolver esse problema , e sua experiência com o desenvolvedor é com uma delas. Com o .NET, seu binário é executado em um ambiente de tempo de execução portátil (CLR) . Java chama seu ambiente de tempo de execução de Java Virtual Machine . Você só precisa distribuir um binário que funcione em qualquer sistema (pelo menos, qualquer sistema em que alguém já tenha implementado uma JVM ou CLR). Você ainda pode ter problemas de portabilidade, como /
vs \
separadores de caminho, ou como imprimir, ou detalhes de layout da GUI, é claro.
Muitos softwares são escritos em idiomas totalmente compilados no código nativo . Não há .net
bytecode ou java, apenas código de máquina nativo para a CPU em que será executado, armazenado em um formato de arquivo executável não portátil. C e C ++ são os principais exemplos disso, especialmente no mundo Unix. Obviamente, isso significa que um binário precisa ser compilado para uma arquitetura específica da CPU.
As versões da biblioteca são outro problema . As bibliotecas podem e muitas vezes mantêm a API no nível de origem estável ao alterar a ABI no nível binário. (Consulte Diferença entre API e ABI .) Por exemplo, adicionar outro membro a um opaco struct
ainda muda de tamanho e requer uma recompilação com cabeçalhos para a nova versão da biblioteca para qualquer código que aloque espaço para essa estrutura, seja ela dinâmica (malloc ), estático (global) ou automático (local na pilha).
Os sistemas operacionais também são importantes . Um sabor diferente de Unix para a mesma arquitetura de CPU pode ter diferentes formatos de arquivos binários, a ABI diferente para fazer chamadas de sistema e valores numéricos diferentes para constantes como fopen(3)
's O_RDONLY
, O_APPEND
,O_TRUNC
.
Observe que mesmo um binário vinculado dinamicamente ainda possui algum código de inicialização específico do SO que é executado anteriormente main()
. No Windows, é isso crt0
. Unix e Linux têm a mesma coisa, onde algum código de inicialização do C-Runtime está estaticamente vinculado a todos os binários. Eu acho que, em teoria, você poderia projetar um sistema em que esse código também fosse dinamicamente vinculado, e parte da libc ou do próprio vinculador dinâmico, mas não é assim que as coisas funcionam na prática em qualquer sistema operacional que eu conheça. Isso resolveria apenas o problema de ABI da chamada de sistema, não o problema de valores numéricos para constantes para funções de biblioteca padrão. (Normalmente, as chamadas do sistema são feitas através das funções do invólucro libc: Um binário x86-64 Linux normal para a fonte que usa mmap()
não inclui a syscall
instrução, apenas umcall
instruções para a função wrapper libc com o mesmo nome.
Isso é parte do motivo pelo qual você não pode simplesmente executar os binários do i386-FreeBSD no i386-Linux. (Por um tempo, o kernel do Linux tinha uma camada de compatibilidade de chamada do sistema. Acho que pelo menos um dos BSDs pode executar binários do Linux, com uma camada compatível semelhante, mas é claro que você precisa de bibliotecas do Linux.)
Se você quisesse distribuir binários, seria necessário criar um separado para cada combinação de CPU / OS-sabor + versão / versão-biblioteca-instalada .
Nos anos 80/90, havia muitos tipos diferentes de CPU em uso comum para sistemas Unix (MIPS, SPARC, POWER, PA-RISC, m68k etc.) e muitos tipos diferentes de Unix (IRIX, SunOS, Solaris, AIX, HP-UX, BSD etc.).
E isso é apenas sistemas Unix . Muitos pacotes de código-fonte também compilariam e funcionariam em outros sistemas, como VAX / VMS, MacOS (m68k e PPC), Amiga, PC / MS-DOS, Atari ST, etc.
Ainda existem muitas arquiteturas de CPU e sistemas operacionais, embora agora a grande maioria dos desktops esteja x86 executando um dos três principais sistemas operacionais.
Portanto, já há mais combinações de CPU / OS do que você pode imaginar, mesmo antes de começar a pensar nas dependências de bibliotecas de terceiros que podem estar em versões diferentes em sistemas diferentes. (Qualquer coisa que não seja fornecida pelo fornecedor do SO deve ser instalada manualmente.)
Todos os caminhos que são compilados no binário também são específicos do sistema. (Isso economiza RAM e tempo em comparação com a leitura de um arquivo de configuração na inicialização). Os sistemas Unix da velha escola geralmente tinham muitas coisas personalizadas à mão, então não há como você fazer suposições válidas sobre o que é onde.
A distribuição de binários era totalmente inviável para o Unix da velha escola, exceto para os principais projetos comerciais que podem se dar ao luxo de construir e testar todas as principais combinações .
Mesmo fazendo binários por apenas i386-linux-gnu
e amd64-linux-gnu
é difícil. Muito tempo e esforço foram gastos em coisas como a Linux Standard Base para possibilitar binários portáteis . Mesmo binários vinculados estaticamente não resolvem tudo. (por exemplo, como um programa de processamento de texto deve ser impresso em um sistema RedHat vs. um sistema Debian? Como a instalação deve adicionar um usuário ou grupo para um daemon e organizar seu script de inicialização após cada reinicialização?) exemplos, porque a recompilação a partir da fonte não os resolve.
Além de tudo isso, antigamente a memória era mais preciosa do que é agora. Deixar de lado os recursos opcionais em tempo de compilação pode criar binários menores (menos tamanho de código) que também usam menos memória para suas estruturas de dados. Se um recurso exigir um membro extra em todas as instâncias de um determinado item class
ou struct
rastrear algo, a desativação desse recurso reduzirá o objeto em 4 bytes (por exemplo), o que é bom se for um objeto ao qual o programa aloca 100k.
Atualmente, os recursos opcionais em tempo de compilação são usados com mais frequência para tornar opcionais bibliotecas extras. por exemplo, você pode compilar ffmpeg
com ou sem libx264
, libx265
, libvorbis
, e muitas outras bibliotecas para vídeo específico / codificadores de áudio, a manipulação das legendas, etc. etc. Mais comumente, um monte de coisas podem ser compilados com ou sem libreadline
: se estiver disponível quando você executa ./configure
, o O binário resultante dependerá da biblioteca e fornecerá edição de linha sofisticada ao ler de um terminal. Caso contrário, o programa usará algum suporte de fallback para ler apenas linhas do stdin com fgets()
algo assim.)
Alguns projetos ainda usam recursos opcionais para omitir códigos desnecessários por motivos de desempenho. por exemplo, o próprio kernel do Linux pode ser construído sem o suporte a SMP (por exemplo, para um sistema incorporado ou uma área de trabalho antiga); nesse caso, grande parte do bloqueio é mais simples. Ou com muitos outros recursos opcionais que afetam parte do código principal, não apenas deixando de fora os drivers ou outros recursos de hardware. (Embora as opções de configuração específicas do arco e do hardware sejam responsáveis por grande parte do código fonte total. Consulte Por que o kernel do Linux tem mais de 15 milhões de linhas de código? )