Cross-compiling fácil fácil
Postado por Osvaldo Santana
Como eu já contei no post anterior no meu novo trabalho a gente tem que lidar com cross-compiling (compilação cruzada) o tempo todo. A idéia da compilação cruzada é simples: você compila um programa P em uma plataforma A e o binário produzido deverá rodar em uma plataforma B.
O conceito é simples, o seu funcionamento na teoria também. Para compilar um típico programa Linux em um computador x86 para rodar na plataforma ARM bastaria ter o toolchain, que é o conjunto de ferramentas que engloba o binutils (onde fica o linker) o gcc (onde fica o compilador C/C++) e em algumas bibliotecas básicas já como binários ARM (a libc é uma delas).
O problema do ovo e da galinha dificulta um pouco a construção de um toolchain (você precisa compilar o compilador) mas não é incomum que esses toolchains já sejam distribuídos com a plataforma ‘alvo’, logo, esse problema não é muito grande.
Com cross-toolchain já instalado a teoria diz que bastariam os seguintes comandos (assumindo que a nossa plataforma alvo seja ARM) para compilar um programa:
$ ./configure --host=arm-linux $ make $ make install
Com algumas pequenas variações disso conseguiríamos fazer a compilação cruzada de ‘todo o Linux’, mas na prática a teoria não funciona…
O que acontece é que um grande percentual das aplicações (regra e não exceção) simplesmente ignora o fato de que no futuro elas serão submetidas à compilação cruzada e simplesmente não funcionam nessas circunstâncias.
O Python é um desses programas. O interpretador compila perfeitamente, mas as extensões em C da biblioteca padrão não. O problema é que o Python usa o módulo distutils para executar tal tarefa e o mesmo é feito em Python. Neste caso precisamos de um Python ARM para executar a segunda fase do processo de build. Como executar um Python ARM em uma máquina x86?
Uma das maneiras é compilar primeiro um interpretador Python x86, renomeá-lo para algo como ‘hostpython‘, depois compilar um interpretador Python para ARM e aplicar uns patches no Makefile.in do Python para que ele chame o ‘hostpython‘ para compilar as extensões C. Mas os efeitos colaterais dessa solução são enormes porque existem extensões que usam bibliotecas do sistema (OpenSSL, Socket, SQLite, …) e o distutils não irá procurá-las no lugar correto pois não sabe o que é compilação cruzada.
Aí então entra uma técnica de transparência de CPU que aprendi a fazer com a turma do projeto Scratchbox (aperfeiçoada pela turma do projeto Mamona) que é bem simples e permite fazer compilação cruzada sem modificar nada nas aplicações que estão sendo compiladas.
A idéia é usar o binfmt do Linux para dizer que todos os binários ARM deverão ser executados pelo qemu (pegadinha 1: esse qemu deve ser estático e estar instalado dentro do chroot no path definido nas configurações do binfmt) e criar um ambiente chroot com tais binários.
Dentro desse ambiente todos os binários ARM rodam com o qemu e todos os binários x86 rodam nativamente na sua plataforma (assumindo que ela é x86) sem que você sequer note a diferença entre o funcionamento deles. Desta forma podemos colocar então o nosso cross-toolchain dentro desse chroot fingindo ser um toolchain nativo ARM (tem uma pegadinha aqui: esse toolchain precisa ser estático e não dinâmico pois as bibliotecas nesse nosso ambiente são ARM e não mais x86).
Você está me perguntando “porque não compilar um toolchain nativo pra rodar dentro desse chroot“? Só por questão de velocidade. O gcc rodaria muito devagar sendo emulado pelo qemu.
Agora é só sair compilando os programas normalmente. Mesmo aqueles que não estão preparados para compilação cruzada:
$ ./configure $ make $ make install
Neste exato momento estou compilando o SQLite3 (após ter terminado o OpenSSL) dentro do ambiente chroot com binários ARM (XScale) que rodam emulados pelo qemu. Tudo isso para que no final eu tenha um Python com Pygame 100% funcional.
Ok. Agora é a hora das notícias ruins:
- O qemu não emula 100% das syscalls, logo, você poderá esbarrar com uma das famigeradas mensagens “Unsuported syscall XX“. Nestes casos verifique se já não existe um patch que implementa o suporte à essa syscall no qemu e recompile-o (lembrando que o qemu precisa ser estático).
- O qemu não lida muito bem com threads, logo, se os scripts de build do seu programa testam threads eles podem quebrar (ou bloquear). Neste caso a sugestão é: retire esses testes dos scripts e assuma que sua plataforma tem sim (ou não) suporte à threads.
- Lembre-se sempre que o kernel não é emulado pelo qemu! Se o seu programa usa ferramentas como o uname, por exemplo, ele irá retornar informações da sua plataforma nativa e não da plataforma emulada. Desenvolvimento para o kernel também não é muito viável.
- Pode acontecer da tua aplicação quebrar ’silenciosamente’. Lembre-se que pode ser o qemu quebrando e ocultando a real causa do problema. Nesses casos utilize as funções de depuração do qemu.
- Lembre-se de ter o /proc e o /sys montados dentro do seu chroot. Alguns programas usam as informações disponíveis nesse lugar para a sua construção. Lembre-se também que esse /proc e /sys são da sua plataforma nativa e não da plataforma emulada, logo, eles poderão fornecer informações incorretas.
- Dica: tenha a sua área de trabalho ($HOME) montada via mount –bind dentro de seu chroot para que você possa ter vários chroots compartilhando o mesmo $HOME.
O sistema de testes dos meus sonhos
Postado por Osvaldo Santana
O uso de testes no desenvolvimento de software já vem me acompanhando desde que trabalhei com Smalltalk na Objective Solutions. O sistema de testes que eles tinham lá chegava muito próximo do que eu idealizo para um sistema de testes.
Recentemente eu li o livro Test-Driven Development do Kent Beck e resolvi experimentar TDD de forma radical em um projeto pessoal em que estou desenvolvendo em Python. O meu sentimento geral sobre o uso de TDD é a de que estou programando num ritmo bem mais lento do que costumo ter mas com a certeza absoluta de que estou seguindo pelo caminho correto.
Como os detalhes de implementação desse projeto não estão muito claros em minha cabeça o uso de TDD está se mostrando ideal, mas não recomendo para o desenvolvimento de uma aplicação na qual você já tenha uma boa idéia de como implementar pois ela realmente ‘desacelera’ o seu ritmo. Mas atenção: estou desaconselhando o uso de TDD e não a criação de testes! Nos projetos onde é possível desenvolver testes é fundamental fazê-lo.
Mas voltando ao assunto deste post eu adoraria ver uma ferramenta para Python com as seguintes funcionalidades:
- Suportar testes escritos com xUnit (unittest) e doctests.
- Vir junto com framework xUnit mais poderoso que o unittest padrão do Python. O py.test tem umas idéias legais. Juntar outras bibliotecas auxiliares, tais como o mocker, também seria legal.
- Ter um sistema de discovery automático para testes (ex. nose).
- Ter uma interface texto para uso em servidores de testes.
- Ter uma interface gráfica para uso do desenvolvedor.
- Suportar execução distribuída de testes. Ainda não preciso disso mas estou prevendo que precisarei no futuro.
- Integração com algum software de lint (ex. pylint).
- Integração com sistemas de teste de cobertura (ex. coverage).
- Emitir relatório sobre a qualidade do código (baseado na análise do lint), da cobertura dos testes, do tempo de execução dos testes e, em caso de falha, o traceback do erro.
- Permitir a execução do pdb caso algum teste falhe.
- Integração com o mecanismo de persistência de objetos (ORM, OODBMS, …) para permitir a criação de savepoints (aka subtransactions) para testes que precisam construir cenários com muitos objetos. Criando esses savepoints um teste poderia fazer um rollback parcial dos objetos criados/alterados pelo teste executado anteriormente. Eu usei essa integração na Objective e garanto que era muito útil além de diminuir o tempo para execução dos testes de forma colossal.

Modelo da interface gráfica dessa aplicação*.
Esse é o sistema dos meus sonhos e acredito que, conforme o desenvolvimento do meu projeto avance, eu acabe com algo muito parecido nas mãos. Mas se alguém quiser começar antes eu prometo que usarei e, caso me sobre tempo, ajudarei no desenvolvimento
Quem sabe um dia esse carinha trabalhe integrado a um SCM, ao reviewboard e a um sistema de issue tracker… mas chega de viagem, hora de voltar ao trabalho.
* Note que ainda está faltando uma Treeview para selecionar o módulo cujo código fonte será exibido.
Gadget da Livraria Cultura
Postado por Osvaldo Santana
Finalmente eu resolvi aprender a mexer com o sistema de gadgets do Google Personalized Pages.
Como sou fascinado por livros e sou afiliado da Livraria Cultura resolvi criar um componente que me mostre uma listagem dos livros mais vendidos e um campo de pesquisa por título.
Não foi fácil. Na verdade foi muito difícil porque o HTML produzido pela Livraria Cultura não é válido, vem com charset incorreto e não tem nenhuma marcação de onde começam e de onde terminam as informações relevantes sobre os livros. Aliás, eu notei esse mesmo problema em outras lojas on-line (mas o da Cultura realmente é o pior).
No lugar deles incentivarem pessoas como eu que estão criando um canal de venda eles preferem nos dificultar. Depois os empresários ficam dizendo que ser empresário no Brasil é difícil, e não é por menos, com essa pouca inteligência realmente fica difícil.
Enquanto isso, nos países “de primeiro mundo”, empresas como a Amazon fornecem Webservices específicamente para facilitar essa tarefa.
Bom, depois desse desabafo aí está a minha “obra”:
Mecânico vs. Engenheiro Mecânico
Postado por Osvaldo Santana
Depois de ler um artigo muito interessante que fala sobre o uso das “super ferramentas” e de acompanhar a fase de iniciação de dois ex-desenvolvedores Delphi que decidiram investir no aprendizado de Python eu andei pensando que existem muitos programadores no Brasil que julgam ser importante se tornar apenas um programador “mecânico” e não um programador “engenheiro mecânico”.
Eu programo há muito tempo. Desde a época em que os programadores tinham que digitar o número das linhas de um programa para que o computador pudesse saber a ordem de execução das instruções. Depois disso eu “evolui” e passei a ter como companheiro um bom editor de textos e uma dupla inseparável de compilador e “linkeditor” que eram executados de maneira quase manual.
As ferramentas de desenvolvimento foram então evoluindo e chegamos então à era das IDEs e super-IDEs que transformavam as intermináveis horas de digitação de um programa em uma meia-dúzia de cliques de mouse.
Tudo muito bom, moderno, legal e ágil, mas ao mesmo tempo emburrecedor. Emburrecedor porque deste ponto em diante os programadores passaram a usar apenas as ferramentas e a usar somente aquilo que elas lhes ofereciam. Passamos então a “consertar os carros” usando ferramentas prontas e pré-fabricadas e deixamos de entender os conceitos físicos e científicos envolvidos no funcionamento dos “motores” desses carros.
A dependência dessas ferramentas e o decorrente emburrecimento dos desenvolvedores fez com que a adaptação desses mesmos desenvolvedores às novas tecnologias se tornasse muito mais complexo do que era antes.
O que eu quero dizer com isso tudo é que de fato é muito bom você ter ferramentas poderosas para agilizar o seu trabalho mas se você se preocupar em usar apenas essas ferramentas você estará limitando suas possibilidades ao que essa ferramenta oferece.
Quando você decidir se tornar um desenvolvedor pense se você pretende ser só um “mecânico” preocupado em usar as ferramentas prontas ou se você quer ser um “engenheiro mecânico” que além de usar as ferramentas também são capazes de criar novas ferramentas.
E vejam: eu não sou contra IDEs, mas acho que os programadores deveriam ser capazes de trabalhar sem elas.
Eu programo em Foo, eu sou superior!
Postado por Osvaldo Santana
Essa eu recebi de um amigo do trabalho.

Viram? Nós somos inferiores à programadores Perl!
Isso é uma afronta…
Duck Typing
Postado por Osvaldo Santana
Lendo os comentários de uma notícia que foi postada recentemente no BR-Linux eu pude observar que algumas pessoas não entendem muito bem o que é duck typing.
Ao contrário do que muitas pessoas pensam duck typing não é um mecanismo disponível em linguagens de programação que usam tipagem dinâmica mas sim uma técnica (ou prática) de desenvolvimento. Essa técnica é explicada da seguinte forma:
Se um objeto anda como um pato e faz quack como um pato então ele é um pato.1
O problema dessa explicação é que ela não fornece muitos elementos úteis para que as pessoas possam entender exatamente como isso funciona então irei recorrer à outra citação extraída do livro Design Patterns:
Program to an interface, not an implementation. (Programe para uma interface, não para uma implementação).
Duck Typing é uma técnica que funciona com qualquer linguagem de programação com suporte ao paradigma OO e diz basicamente que se o seu objeto responde à uma determinada mensagem (chamada de método) característica de um determinado tipo de objeto então esse objeto também pode ser considerado do mesmo tipo.
Trocando em miúdos: Se eu tenho um objeto do tipo “Conta” e um objeto do tipo “Lançamento” e ambos os objetos respondem ao método “.cancela()” pouco me importa se eles são derivados de uma classe em comum ou se ao definir a classe deles eu especifiquei algo como “implements Cancelable” :), o que me importa é que quando eu fizer “objeto_cancelavel.cancela()” esse objeto será cancelado.
Alguns defensores da tipagem estática podem dizer: “mas e se o objeto não implementar o método “.cancela()” e eu chamá-lo o meu programa vai quebrar!”. Sim, vai, e é exatamente isso que teria que acontecer, afinal de contas se você está tentando “cancelar” um objeto que não pode ser cancelado (não implementa “.cancela()”) é muito provável que o seu programa esteja com algum bug que precisa ser corrigido, implementando o método “cancela()” no objeto ou não chamando esse método onde ele está sendo chamado, já que essa chamada estaria violando o polimorfismo.
Então, para terminar, é necessário lembrar que duck typing não é um tipo de tipagem (tocradilho não intencional) dessa ou daquela linguagem de programação. É apenas a forma com que você faz uso das interfaces de seus objetos.
1O nome duck typing surgiu a partir dessa explicação.






