A pensar:
Disponível com legendas em português e outras línguas.
Clique em View Subtitles e depois em portuguese (Brazil).
Novo Blog
Novo endereço
https://blog.nilo.pro.br
sexta-feira, 13 de agosto de 2010
domingo, 11 de julho de 2010
Como não desenvolver software
Eu nunca vi dois programadores desenvolverem o mesmo programa da mesma forma. Dizer o que é certo em desenvolvimento de software é muito difícil, tudo depende de quais correntes de pensamento você segue. Orientação a objetos, script-maníacos, programação funcional? Tem espaço para todo mundo e ninguém escapa em maior ou menor grau de vários sintomas aqui descritos.
Algumas coisas são tão ruins que mesmo em um assunto tão "quente" como desenvolvimento de software, pode-se achar um consenso. Eu vou relatar algumas das más práticas que conheci, pois acredito que elas são bastante comuns, especialmente com as novas tecnologias, Internet e código open source. Quanto maior o time de desenvolvimento, mais frequentes ficam esse pequenos problemas. O intuito deste post é relatar algumas experiências com o mesmo rigor científico de uma conversa de bar. Todas as afirmações são relativas e dependem do humor de quem as lê. Tudo deve ser encarado com várias pitadas de sal e bom humor. Eu avisei :-D
Parece comédia, mas tudo isso é real. Profissão alguma é perfeita ou sem dificuldades, a diferença em informática é que nós criamos e retiramos os obstáculos no nosso caminho :-). Normalmente misturando novas soluções com antigos problemas numa velocidade fantástica.
Algumas coisas são tão ruins que mesmo em um assunto tão "quente" como desenvolvimento de software, pode-se achar um consenso. Eu vou relatar algumas das más práticas que conheci, pois acredito que elas são bastante comuns, especialmente com as novas tecnologias, Internet e código open source. Quanto maior o time de desenvolvimento, mais frequentes ficam esse pequenos problemas. O intuito deste post é relatar algumas experiências com o mesmo rigor científico de uma conversa de bar. Todas as afirmações são relativas e dependem do humor de quem as lê. Tudo deve ser encarado com várias pitadas de sal e bom humor. Eu avisei :-D
- Over-engineering: quem nunca viu um pequeno exemplo com 1000 linhas de código não sabe do que estou falando... era para ser um "Hello World"... 1000 linhas depois ninguém sabia o que era. Um programa de computador precisa ser razoavelmente robusto a falhas, mas existe uma grande diferença entre uma aplicação crítica e uma aplicação simples, que pode simplesmente parar de funcionar e dar uma mensagem de erro! O sintoma se apresenta quando a confiabilidade do Sol começa a ser posta em jogo. E a pergunta cruel surge: quando o Sol explodir, o que faremos? Variações desta realidade são exigências como confiabilidade e disponibilidade de sistemas web comparáveis às do Facebook ou do Google, exceto que você ainda não tem o tráfego nem o dinheiro destes sites. Variações incluem o desejo incotrolável de utilizar o maior número de patterns no maior número de classes possível. Nada pode ser feito em apenas uma classe. Classes ficam melhor em dúzias. Porque deixar para um futuro refactoring o que já pode ser feito hoje?
- Paralisia por análise: quando os programadores se lançam na quest máxima: a busca DO framework. Perdem-se às vezes meses com experimentos para simplesmente escolher um novo framework, ignorando toda a experiência anterior do time, ou que o tempo perdido não volta mais. Em informática é muito difícil escapar desta situação, principalmente com software open source, constantemente atualizado e melhorado. Parar de trocar pode simplesmente significar a morte do programador, ou sua condenação ao castigo máximo, como programar para sempre em Clipper ou Cobol :-D A questão é que uma decisão deve ser tomada e normalmente nenhum framework oferece solução para tudo, ou torna tudo simples, fácil e claro. Algumas pessoas buscam soluções que "não dão trabalho", mas esquecem que o fruto desta automatização é normalmente seguir padrões e conceitos do novo framework, conceitos que demandam tempo para ser aprendidos. Uma mistura de paralisia por análise com over-engineering é quando o time resolve encontrar uma solução perfeitamente escalável, adaptável a qualquer cliente e que possa rodar por uns 10 anos com pouca manutenção... ou seja, buscam alcançar o nirvana da programação. Isso não é desculpa para não analisar as melhores alternativas para resolver o problema, mas esta análise não deve durar mais tempo que o desenvolvimeno da solução em si.
- Bala de prata: é quando o time decide resolver tudo com a mesma tecnologia sempre. Normalmente com C ou Java, mas começam a aparecer mais e mais casos com linguagens scripts. A verdade é que não existe bala de prata (no sentido de solução universal), nem lobizomem. Toda solução deve ser cuidadosamente analisada, dentro de um prazo bem definido, para não cair no problema da paralisia. Já se dizia há muito tempo: "Se a única ferramenta que você conhece é um martelo, tudo vira prego".
- Time Bom Bril: é o sonho de todo gerente de software. Um time multiplataforma, multilinguagem, com profissionais altamente inteligentes, simpáticos e dóceis. Entre outras características deste tipo de alucinação é usar "O time" em todo e qualquer tipo projeto, ignorando o conhecimento de domínio. Hospital, eleições, lançamento de foguetes, hardware? Não tem problema, "O time" resolve. Além deste tipo de patia causar danos ao pobres membros do time, a perda de produtividade é enorme, causando a aparição do folclórico Jack-of-all-trades-master-of-none. Ninguém consegue dominar tudo e alcançar resultados excelentes em tudo, isso é ilusão. Profissionais altamente capazes podem aprender muito rápido e superar o resultado de times ordinários, mas isso tem um preço. A pulverização de esforços e a constante pressão podem queimar um time de desenvolvimento muito rápidamente.
- Cada um por si e ninguém por todos: muitos chefes atrapalham, mas quando não há algum chefe, todos são chefes! Essa é fácil de se cair quando se tem um time super star ou um dream team em determinada tecnologia. Como todos são profissionais de alto valor e experientes, independência total é dada a cada membro do time. No final, ninguém se entende, pois cada membro do tal time resolveu utilizar as práticas e conceitos da corrente de desenvolvimento que mais lhe agrada. No fim, a empresa fica com uma salada de códigos-fonte, provavelmente escritos em várias linguagens de programação, frameworks diferentes, etc. Um pouco de ordem ajuda a manter a sanidade do time.
- Commits preciosos: alguns times são intruídos a só "comitarem" suas alterações após finalizarem sua tarefa de implementação. Isso funciona bem e evita que código inacabado perturbe o desenvolvimento de outros desenvolvedores devido a anomalias no build. O problema é que algumas pessoas não dividem as tarefas em espaços razoáveis de tempo. O sintoma são commits quinzenais ou mesmo mensais! Indagados sobre a prática, alguns podem responder que fazem isso para não poluir o repositório com commits pequenos. A questão toda é que o próprio desenvolvedor precisa de um sistema de controle de versão para seu desenvolvimento inacabado. Se um sistema de controle de versão distribuído, como o GIT ou Mercurial é usado, pode-se resolver este tipo de problema com branches locais. Com SVN não tem muito jeito... você tem que comitar após testar e garantir que o que foi commitado não quebra o build. Se algo estiver errado, o sistema de controle de versão tem ferramentas para corrigir estas falhas. Além disso, fazer o merge após um mês de desenvolvimento pode ser muito trabalhoso, pois a probabilidade de se ter alterado o mesmo arquivo que outras pessoas é alta, gerando conflitos.
- Repo killer: quando um membro decide comitar e atualizar apenas partes do repositório, privando os outros membros do time de trabalharem com a mesma versão do código. Essa é inexplicável. Uma variação é o desenvolvedor que escolhe a dedo o que atualizar, esquecendo de incluir novos arquivos e mudanças que nem ele mesmo lembra que fez. Esse problema é uma bomba relógio, pois as diferenças entre os repositórios será evidente cedo ou tarde.
- Builds locais: poupar o dinheiro de uma máquina de build pode ser a pior economia a fazer em um time de software. Se apenas uma pessoa desenvolve o código, você só saberá que tudo foi comitado ou não no dia que ela sair da empresa. Aí começa a caça aos arquivos perdidos. Outro grande problema é a não documentação do ambiente de desenvolvimento, mas esse problema aparece mais rápido, normalmente quando o HD do desenvolvedor pifa e ele passa uma semana para lembrar tudo que usava (IDE's, bibliotecas, paths, etc). Esse problema também expõe um mais grave: a falta de testes.
- Ausência de testes: uma das causas de se perder um usuário de seu sistema é transformá-lo em testador. Para isso, você não desenvolve testes automatizados e normalmente não realiza qualquer controle de releases. Isso gera inúmeros problemas durante todo ciclo de desenvolvimento e o único teste acaba sendo feito pelo compilador. Um código que compila está longe de estar livre de erros. Não controlar os erros já reportados pelo usuário é grave. Não corrigi-los é pior e ressuscitar bugs antigos por falta de testes é imperdoável.
- Zipmania: desenvolvedores param de usar o sistema de controle de versão e começam a trocar Zips com nomes poderosos por IM: sistema20100610_XPO.zip. O vetor de transmissão é um desenvolvedor suicida que resolve fazer a integração na máquina de outro desenvolvedor, antes de fazer os devidos commits, para "evitar conflitos". Fica melhor ainda quando eles começam a criar zips para "congelar" um release e fazer "backup".
- Variáveis com apenas dois ou três caracteres: essa é muito boa. Um dia você será castigado, tendo que rastrear código em C que usa strtok e variáveis como z, zz e zzz! Fica mais interessante quando esse código é responsável por crashes imprevisíveis no código, do tipo null pointer/segfaults :-D. O pior é que o carrasco pode ter sido você mesmo! É sempre bom comentar o código, principalmente quando o método implementa algo mais complexo que 10 linhas. Mesmo que você não comente, o minimo que pode ser feito é utilizar nomes de variáveis que ainda signifiquem algo dentro de 6 meses ou 2 anos.
- Gotos: código em C com goto ninguém merece. Tirando raríssimas exceções, usar goto's em C é imperdoável. Fica ainda pior quando os "labels" tem sempre o mesmo nome, em várias funções e o tal goto é escondido por macros. Eu aprendi programar nos anos 80, gotos eram a única saída em Basic, mas já eram abominados em C e Pascal há bastante tempo e mesmo antes (Dijkstra, 1968). Para programadores modernos, um goto é simplesmente um atentado a programação estruturada.
- A praga do log: aqui os sintomas são diferentes dependendo da linguagem. Em C/C++ você pode usar o pré-processador para esconder seu log de DEBUG ou fazer a coisa certa e usar um framework descente. Em Java, você tem os loggers da vida. O pior que você pode fazer é usar múltiplos System.outs ou printfs para fazer o log na mão. A praga do log é que mesmo usando um framework, você acaba espalhando linhas de log para tudo e usando apenas dois ou três níveis de log. No final, você tem arquivos de log gigantescos que chegam mesmo a retardar a execução do programa. Eu já procurei causas de lentidão em sistemas Java para descobrir que tínhamos deixado o log das bibliotecas em C ligado! Simplesmente, cada linha do log abria, escrevia, dava um flush e fechava o arquivo na mesma linha, claro, escondendo tudo isso numa macro! C hoje em dia é tão rápido que você pode fazer barbaridades, mas quando você loga deste jeito dentro de um XML parser... a coisa explode.
- Ignorar mensagens de erro do compilador: essa aqui é um prelúdio para o sofrimento. Se no gcc seu código não compila com -Wall ou se você é o rei dos pragmas do Visual C++, lembre que se estas mensagens fossem realmente pouco importantes, elas não seriam geradas pelo compilador! No Java, usa-se o -Xlint para ter warnings mais precisos. O importante é que seu build seja limpo! Isso faz com que qualquer erro seja rapidamente notado. O mesmo para os logs. Se tudo é mostrado na tela, você simplesmente pára de olhar, pois se acostuma a ver lixo ou mensagens que não são interessantes.
- Localização tabajara: ao trabalhar com código para múltiplas línguas, melhor não improvisar. Java resolve este tipo de problema muito bem e o Python na sua versão 3 melhorou bastante. Misturar strings em páginas de código diferentes é fatal! Fazer localização com ifs no código é pior ainda. Até a Microsoft tenta forçar o uso de UNICODE no Visual C++. O pior é ignorar diferenças de codificação e mesmo de tratamento de mensagens. Além da ordem das palavras, línguas diferentes tem critérios também diferentes para ordenação de caracteres, plural e letras maiúsculas, por exemplo.
- Build scripts escritos em Perl e batches: quem ainda trabalha com Perl merece todo castigo :-D O problema são pessoas que não utilizam Perl e que tentam fazer scripts de build em Perl. O mais legal é quando o build é multiplataforma é os tais scripts geram código em C ou C#, sem nada dizer. Para melhorar a festa, pode-se utilizar múltiplos scripts (multiplicados pelo padrão: copy and paste, rename). Agora a cereja do sorvete é fazer isso tudo usando Windows ou Cygwin.
- Babel: por que utilizar apenas uma linguagem de programação? Seu projeto pode ter muitas, você pode até deixar seus programadores escolherem a linguagem de programação mais adaptada a cada tarefa e depois reunir isso tudo via SOAP. Usar Perl com qualquer outra coisa é trabalhar contra a empresa. C/C++ e Java, tudo bem, um pouco de Python ok. Mas se seu código é realmente escrito partes em Basic, partes em C/C++/C# e você resolveu colocar a parte concorrente em F# é porque você já vive o sonho do .Net. O mesmo acontece em Java: Jython ou JRuby, Groovy e Scala. Você pode misturar como quiser, mas a manutenção do código vai ser um pesadelo. Quanto mais linguagens você utilizar, mais difícil será encontrar alguém capaz de trabalhar com todas estas linguagens. Lembre-se que toda linguagem evolui, forçando um esforço arqueológico para entender o que a versão X fazia com a versão Z. Melhor evitar ou pedir aprovação do alto conselho Klingon antes de começar a construir sua torre. E por que apenas uma torre? Você pode desenvolver seu código para múltiplas plataformas, para adicionar um pouco de tempero. Mesmo com Java, faz-se milagre: J2SE, Java ME CDC e Java ME CLDC... a festa! Com C# você pode brincar com o compact framework e por que não com Mono?
- Demos: demo é coisa do cão :-D Quem trabalha comigo ouve essa todo dia. O problema de "demos" que elas são normalmente escritas para rodar apenas uma vez e num curto espaço de tempo. O que leva ao próximo problema. Uma demo é diferente de um protótipo. Um protótipo normalmente é criado para resolver um problema específico ou para demonstrar como os analistas entenderam o problema, um instrumento para refinar e melhorar programas. Uma demo é normalmente criada para demonstrar o domínio de uma tecnologia, é um PPT executável e com código fonte! Normalmente elas resistem a aposentadoria e tem a tendência a evoluir. O ambiente propício para proliferação é a véspera de exposições e eventos.
- Códigos escritos para rodarem apenas uma vez: não acredite nisso. Uma vez que um programa é escrito, o universo conspira para que ele seja mantido por muito tempo. Quase nunca você vai escrever código para rodar apenas uma vez. Se for o caso, tenha certeza de apagar o bicho logo depois da primeira execução, só para garantir.
- XML Doom: alguém resolve que tudo deve ser escrito em XML. Eu já via até métodos que recebem os seus parâmetros no formato mágico que começa com X. O mais interessante do XML é que ele foi criado para facilitar o intercâmbio de informações e também ser legível tanto por computadores quanto por seres humanos. A ideia era poder ter um formato simples, baseado em UTF, ou pelo menos ciente dos inúmeros problemas de internacionalização e de representação de dados binários em formato texto. A coisa começou a desandar quando começamos a escrever arquivos XML na mão, para facilitar o parsing de arquivos de configuração. O XML que deveria ser gerado pelo computador, acabou sendo gerado manualmente. Alguns conseguem ter a sorte de ter editores de textos adaptados e mesmo ferramentas que ajudam na árdua tarefa de criação destes monstros. Uma ideia muito boa que acabou sendo abusada. XML não deve ser usado pra tudo.
- Copy and Paste hell: essa é a pior de todas. Se você copia e cola o mesmo código várias vezes, algo está errado. Ou você não usa os recursos de template de seu IDE (aqueles que transformam syso em System.out.println()) ou você está realmente perdido. Grave mesmo é se você copia grandes partes de código, criando métodos gigantes. Programas criados com alta participação de copy and paste demonstram um problema grave de modularização e normalmente não isolam conceitos adequadamente. Lembre-se que cada código copiado e colado terá os mesmos problemas do código de origem, se você tiver sorte, fazendo com que alterações e modificações tenham que ser propagadas para todas as cópias. É realmente um desafio a sua memória. Sempre se esquece um ou dois... e lá estarão os problemas. Duro mesmo é corrigir o mesmo problema várias vezes. Além disso o tamanho do programa cresce assustadoramente rápido. Depois de ver código em javascript com 10000 linhas por arquivo você fica com medo desta síndrome.
Parece comédia, mas tudo isso é real. Profissão alguma é perfeita ou sem dificuldades, a diferença em informática é que nós criamos e retiramos os obstáculos no nosso caminho :-). Normalmente misturando novas soluções com antigos problemas numa velocidade fantástica.
quarta-feira, 9 de junho de 2010
Lendo livros em formato PDF
Um problema que sempre tive com livros em formato PDF é marcar o ponto onde parei de ler para depois continuar. Nos últimos anos, livros em formato PDF se popularizaram e eu consumo principalmente os livros da Pragmatic Programmers neste formato. Um simples documento PDF de até 5 ou 10 páginas pode ser lido inteiramente em poucos minutos, mas um livro de 500 ou 600 páginas não pode ser consumido de uma só vez. É nesses casos que um leitor adaptado a esta tarefa é necessário.
No Mac eu vinha usando o Skim, um leitor de PDF que permite fazer anotações e outras modificações no PDF que só o Acrobat da Adobe permitiriam, a diferença é que o Skim é open souce. Com o Skim, pode-se utilizar uma barra de leitura que ajuda muito na hora de ler, principalmente para quem pula linhas como eu :-D
Mas como todo grande aplicativo de Mac, o Skim não tem versão para Ruindows e também não grava a última página de cada PDF... tornando a leitura de livros difícil. Para contornar este tipo de problema, eu acabei por utilizar anotações no meu telefone para registrar a página que parei em cada livro, verdadeiramente low tech.
Semana passada eu encontrei o Adode Digital Editions, gratuito e multiplataforma, desenvolvido pela mesma companhia que inventou o formato PDF. Ele foi criado para permitir acesso aos livros digitais da plataforma e-book da Adobe, mas também pode ser utilizado para ler livros já baixados em formato PDF ou ePub. A grande vantagem é poder utilizar o recurso de biblioteca, permitindo juntar os PDFs espalhados pelo disco rígido em uma só interface, organizar estes PDFs em pastas e marcar onde paramos de ler facilmente. No Digital Editions, quando reabro um PDF ele está exatamente na mesma posição que deixei da última vez. A interface é bem limpa e facilita a leitura, tendo apenas os controles básicos de navegação e visualização do PDF e opcionalmente o conteúdo a esquerda.
Ainda não experimentei em vários micros, mas como o recurso de criar uma conta na Adobe, acredito que a sincronização seja possível. Outros recursos parecem bem interessantes, como a possibilidade de emprestar livros. Por enquanto, a funcionalidade de marcar o último ponto de leitura e o tamanho de apenas 19Mb x 195Mb do Acrobat Reader para Mac já são bem interessantes.
No Mac eu vinha usando o Skim, um leitor de PDF que permite fazer anotações e outras modificações no PDF que só o Acrobat da Adobe permitiriam, a diferença é que o Skim é open souce. Com o Skim, pode-se utilizar uma barra de leitura que ajuda muito na hora de ler, principalmente para quem pula linhas como eu :-D
Mas como todo grande aplicativo de Mac, o Skim não tem versão para Ruindows e também não grava a última página de cada PDF... tornando a leitura de livros difícil. Para contornar este tipo de problema, eu acabei por utilizar anotações no meu telefone para registrar a página que parei em cada livro, verdadeiramente low tech.
Semana passada eu encontrei o Adode Digital Editions, gratuito e multiplataforma, desenvolvido pela mesma companhia que inventou o formato PDF. Ele foi criado para permitir acesso aos livros digitais da plataforma e-book da Adobe, mas também pode ser utilizado para ler livros já baixados em formato PDF ou ePub. A grande vantagem é poder utilizar o recurso de biblioteca, permitindo juntar os PDFs espalhados pelo disco rígido em uma só interface, organizar estes PDFs em pastas e marcar onde paramos de ler facilmente. No Digital Editions, quando reabro um PDF ele está exatamente na mesma posição que deixei da última vez. A interface é bem limpa e facilita a leitura, tendo apenas os controles básicos de navegação e visualização do PDF e opcionalmente o conteúdo a esquerda.
Ainda não experimentei em vários micros, mas como o recurso de criar uma conta na Adobe, acredito que a sincronização seja possível. Outros recursos parecem bem interessantes, como a possibilidade de emprestar livros. Por enquanto, a funcionalidade de marcar o último ponto de leitura e o tamanho de apenas 19Mb x 195Mb do Acrobat Reader para Mac já são bem interessantes.
sábado, 5 de dezembro de 2009
Configurando BIND para utilizar o servidor DNS Público do Google
O Google lançou esta semana o Google Public DNS, visando aumentar a velocidade de resolução de nomes de Internet.
A resolução de DNS (Serviço de Resolução de Nomes) é responsável por converter o nome de um site em endereço IP (www.google.com --> 64.233.163.163). Esse serviço é utilizado sempre que seu computador precisa de um endereço de Internet. Cada vez que um novo nome é convertidio em endereço IP, o resultado desta conversão é guardado em uma tabela de acesso rápido em seu computador para agilizar acessos futuros, ou seja, uma tabela cache ou simplesmente cache.
Um exemplo simples é resolver www.google.com. A primeira vez que você acessar o domínio, o serviço de DNS de seu computador vai verificar o cache local e retornar o endereço IP, mas como é a primeira vez que você tenta acessar o Google hoje, não vai encontrar este nome de domínio em sua tabela cache. Quando isso acontece, uma requisição será feita ao seu servidor de DNS.
Um servidor de DNS também faz um cache dos últimos domínios pesquisados, porém vai recorrer aos Root Servers da Internet caso também não tenha o domínio cadastrado. E assim funciona o DNS e toda a Internet como a conhecemos hoje. A grande questão é entender o problema que o Google Public DNS Resolve.
O primeiro problema é que o tempo de acesso entre computadores na Internet pode ser medido em milisegundos (uma eternidade em termos de transmissão de dados :-) ). O tempo de resposta (Round Trip Time - RTT) é simplesmente o tempo que um computador demora para enviar um pacote e receber a resposta de outro computador. Ou seja, o tempo que o computador A leva para receber a resposta do computador B. Um caso prático é o tempo exibido pelo comando ping. O comando ping envia um pacote usando o protocolo ICMP e espera a resposta do outro sistema, imprimindo o tempo entre a requisição e o recebimento da resposta. Quanto mais distantes os computadores, maior o tempo de acesso. Este efeito é causado pelos meios de transmissão (fibra, satélite, cabos, etc) e pelo roteamento da Internet.
Quando um servidor de DNS não encontra um nome de domínio/host em sua tabela cache, este precisa realizar uma nova pesquisa. No caso de www.google.com, a primeira pesquisa será ao servidor responsável pelos domínios .com sobre o endereço dos servidores do Google. Só então seu servidor fará outra pesquisa, desta vez perguntando o endereço de www ao servidor de google.com. A coisa piora quando você tem nomes com vários níveis, mas o servidor faz um cache destas requisições de forma a evitar todo esse trabalho a cada acesso. O tempo de resposta de cada uma destas pesquisas é igual ao tempo de resposta entre os servidores mais o tempo para processar a resposta propriemente dita.
O problema que o Google Public DNS resolve é centralizar de forma Googleludiana toda essa informação. Isso significa poder utilizar a rede global da Google para armazenar e atualizar estas tabelas de conversão. Outro ponto é responder as requisições de forma muito rápida, coisa que o Google é campeão em fazer. Vários provedores de acesso tem mega-caches de domínios, porém a atualização deste cache é um outro problema. O Google pretende resolver o problema dos grandes caches e ainda aumentar o nível de segurança do serviço de resolução de nomes (DNS).
Um exemplo: imagine que o tempo de resposta entre seu servidor de DNS é igual a T. Para responder o endereço IP de www.google.com, teríamos algo como 3T ou 4T. Considere que no caso do Brasil, com redes lentas, esse T é bem grande (>100ms). Pense também que uma simples página web pode ter dezenas de nomes a resolver. Isso causa um atraso enorme ao tempo total de carga da página. Usando o serviço do Google, o tempo de resposta se aproxima de T (uma vez que as tabelas cache do Google tendem a ser maiores e mais rápidas que a do seu pobre ou rico servidor).
Uma descrição completa do problema e da solução pode ser encontrada neste link.
Agora, como usar isso. A tentação pode ser de simplesmente trocar o IP do seu servidor de DNS para 8.8.8.8 e 8.8.4.4. Esta solução é muito interessante e é descrita nas páginas do serviço, mas o melhor mesmo é configurar seu servidor local de DNS para utilizar este serviço. Isto minimiza o tempo de acesso de todas as máquinas da rede e como o tempo de acesso ao servidor local é muito menor que acessar o servidor do Google, o tempo de resolução de domínios ficará bem baixo.
Para quem usa o Bind no Linux sem ter o mesmo configurado como servidor de domínios local ou principal, simplesmente como cache de DNS, basta adicionar as seguintes linhas no /etc/bind/named.conf (o endereço pode mudar de acordo com a sua distribuição Linux).
Adicione as linhas com os servidores do Google (em negrito) logo depois de directory, dentro de options:
options {
directory "/var/bind";
forwarders { 8.8.8.8; 8.8.4.4; };
forward only;
Depois disso, recarrege a configuração do Bind (/etc/init.d/named reload) e tudo feito. Agora você conta com seu cache local e com o serviço público do Google. A diferença é bem perceptível.
Você também pode obter um bom desempenho configurando os servidores do Google em seu roteador de acesso sem fio, mas isso vai depender da velocidade de resposta do mesmo. Vale experimentar!
Você também pode obter um bom desempenho configurando os servidores do Google em seu roteador de acesso sem fio, mas isso vai depender da velocidade de resposta do mesmo. Vale experimentar!
quinta-feira, 29 de outubro de 2009
Windows 7 64 bits no iMac com Snow Leopard - BootCamp 3.0
A vida não é facil para quem tem um iMac e quer instalar o Windows 7 64 bits. A Apple decidiu que os drivers de 64 bits não poderiam ser instalados nos iMacs, só nos MacPros, mas tudo tem jeito :-D. Neste outro post, eu descrevi como instalar a versão 32 bits do Windows 7 RC, mas com o Boot Camp 2.1 do velho Leopard.
O problema é que eu gostei do novo brinquedo e comprei o Windows 7 Edição Familiar Premium. Eu já sabia que o RC seria destruído, então comecei preparado. A caixa chegou na segunda-feira, comprei por 56€ numa super-promoção ainda em Julho na PixMania.be! Por 56€ é uma opção muito interessante, não sei se teria feito o mesmo se tivesse que pagar os 99 que a Microsoft pede hoje. Backup feito, começa a aventura!
Usando a mesma partição do meu Windows 7 RC, eu apenas pedi ao BootCamp 3.0 do Snow Leopard para iniciar o CD de instalação Windows. Fiz o boot e instalei normalmente, usando o setup do Windows. O Windows 7 vem com dois DVDs, um com a versão 32 bits e outro com a versão 64. É bom olhar antes de dar o boot, porque não lembro de nenhum indicador visual de qual dos dois estava instalando (salvo no próprio DVD!).
Instalei o Windows 7 64 bits na mesma partição do Windows 7 32 bits RC, mas para minha surpresa o instalador disse que estaria movendo tudo para uma pasta Windows.OLD. Eu já estava preparado para começar do zero, aguardando a formatação do disco, coisa que não aconteceu. Tudo do Windows 7 RC antigo foi movido para a Windows.Old, inclusive o Program Files e outros diretórios.
Terminada a instalação, não tive qualquer problema no Mac, até precisar instalar o boot camp. A mensagem que apareceu é que a versão 64 bits dos drivers não é suportada na minha máquina: Boot Camp x64 is unsupported on this computer model.
Uma rápida visita no site da Apple, revela que os iMacs não estão na lista dos equipamentos suportados para os drivers de 64 bits do Windows Vista ! Uma rápida busca no Google, revela este artigo. Eu já havia lido outros, mas poucos sobre o problema da restrição do iMac. Meu iMac é modelo 2008. Antes disso, nem adianta tentar. Nao precisa baixar nada de sites estranhos, tudo que você precisa está no DVD do Snow Leopard. Um dos comentários do artigo diz que conseguiu instalar o BootCamp pelo arquivo .msi. Aparentemente é o setup.exe que verifica a restrição aos iMacs. O problema é que clicando no Boot Camp\Drivers\Apple\BootCamp64.msi no Windows Explorer ele não instala. O comentário também diz para executar em linha de comando privilegiada. Aí a coisa ficou esquisita, pois eu já era administrador do sistema. Lembrei que o Windows 7 é um primo próximo do Vista, chato mesmo.
O que precisa fazer:
- A partir do menu iniciar, abra a pasta de acessórios e clique com o botão direito no Command Prompt. Escolha a opção de executar em modo privilegiado.
- Na janela de comandos que abriu, vá para o diretório do BootCamp64, no meu caso o drive D: é o DVD-ROM:
cd "\Boot Camp\Drivers\Apple"
msiexec /i BootCamp64.msi
Depois é só prender o fôlego e ter fé. A instalação do Boot Camp é assustadora. Telas pretas e piscadas alucinantes aconteceram no meu caso, mas é coisa rápida, bips também acontecem. Nada que não assuste durante a instalação de um novo Windows, principalmente instalando drivers do Vista 64 no Windows 7... mas já da para aguardar até os drivers nativos do Windows 7 serem lançados.
Um boa é que mesmo antes do Boot Camp ser instalado eu já tinha rede. Rede com cabo e sem fio funcionaram de primeira após a instalação. O som, o eject e a camera só funcionaram depois de instalar o BootCamp. Alias, o som não está tão bom assim. Uma dica é que o eject funciona pelo Windows Explorer, mesmo antes de instalar o Boot Camp. É só clicar no drive de DVD com o botão direito e selecionar ejetar.
A melhor surpresa foi poder acessar a partição do Mac pelo Windows Explorer. Isso é muito legal, principalmente quando você procura um arquivo que está no outro sistema.
Em relação a utilização de memória, aqui eu perdi uns 200 MB de cara, só trocando de 32 para 64 bits. O Windows 32 pedia uns 400 MB para mostrar o Desktop :-)... o 64 ja começa com 600MB. Era a desculpa que eu precisava para colocar mais 2G :-D
domingo, 25 de outubro de 2009
Capturando a saída interativa do interpretador Python em Python
Tudo tem que ser automatizado. Como estou escrevendo um pequeno livro sobre Python já há muito tempo, tenho que atualizar a versão da linguagem todo ano :-D. Com o Python 3.1 o problema ficou mais sério, pois agora tenho que testar os exemplos do livro com essa nova versão que não é totalmente compatível. O livro está sendo escrito em Latex, usando o TextMate e o Skim no Mac OS X. Eu fiz a besteira de inserir manualmente o código fonte Python no texto Latex, agora tenho que revisar os scripts um a um. Como vou revisar, aproveito para consertar e desta vez gravar os programas Python em arquivos separados.
Resolvi então utilizar o SCONS para ajudar na tarefa. Para isso, um builder customizado precisa ser criado. No SConstruct:
e depois o programa em Python que faz a captura, capture.py:
O script capture.py recebe dois parâmetros: o nome do arquivo Python e o nome do arquivo de saida.
Para testar, crie um arquivo p1.py:
Deve produzir a seguinte saída em p1.pyt:
Se você utilizar este arquivo, não esqueça de modificar as variaveis PYTHON_PATH. Eu uso MacPorts e devo especificar o caminho completo da versão de Python que quero utilizar. Isso evita problemas com outras versões instaladas no sistema, como o Python da Apple. Eu utilizei o Python 3.1 nos exemplos do livro, mas ainda uso o 2.6 para instalar meus módulos no Mac Ports.
Eu espero que isto ajude alguém com o mesmo tipo de problema: capturar a saída interativa de um programa como se fosse um terminal comum.
Resolvi então utilizar o SCONS para ajudar na tarefa. Para isso, um builder customizado precisa ser criado. No SConstruct:
PYTHON_BIN="/opt/local/bin/python2.6" python_output=Builder(action= '$PYTHON_BIN capture.py $SOURCE $TARGET', suffix = '.pyt', src_suffix = '.py') env = Environment(BUILDERS = {'python_output': python_output}) env["PYTHON_BIN"]=PYTHON_BIN env.python_output("p1")
e depois o programa em Python que faz a captura, capture.py:
import pexpect import sys PYTHON_BIN="/opt/local/bin/python3.1" interacao = file(sys.argv[1], "r").readlines() python = pexpect.spawn(PYTHON_BIN) python.logfile_read = file(sys.argv[2], "w") for l in interacao: python.expect([">>> ", "... "]) python.send(l) python.sendline("exit()") python.expect(pexpect.EOF)
O script capture.py recebe dois parâmetros: o nome do arquivo Python e o nome do arquivo de saida.
Para testar, crie um arquivo p1.py:
a=5 b=10 print(a+b) c = a + b d = c * a / 100 print(c,d) for x in range(3): print ("Oi") #Erro: =d=d rrrrr
Deve produzir a seguinte saída em p1.pyt:
Python 3.1.1 (r311:74480, Oct 24 2009, 17:11:04) [GCC 4.2.1 (Apple Inc. build 5646)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> a=5 >>> b=10 >>> print(a+b) 15 >>> c = a + b >>> d = c * a / 100 >>> print(c,d) 15 0.75 >>> for x in range(3): ... print ("Oi") ... Oi Oi Oi >>> #Erro: ... =d=d File "", line 2 =d=d ^ SyntaxError: invalid syntax >>> rrrrr Traceback (most recent call last): File " ", line 1, in NameError: name 'rrrrr' is not defined >>> >>> exit()
Se você utilizar este arquivo, não esqueça de modificar as variaveis PYTHON_PATH. Eu uso MacPorts e devo especificar o caminho completo da versão de Python que quero utilizar. Isso evita problemas com outras versões instaladas no sistema, como o Python da Apple. Eu utilizei o Python 3.1 nos exemplos do livro, mas ainda uso o 2.6 para instalar meus módulos no Mac Ports.
Eu espero que isto ajude alguém com o mesmo tipo de problema: capturar a saída interativa de um programa como se fosse um terminal comum.
sábado, 24 de outubro de 2009
Aventuras com FSCK
Back-ups sempre vão bem... essa semana acabei perdendo 2 HDs. Um no PC, consegui retirar todos os dados e transferir para um servidor remoto. O outro, meu Ubuntu, Tambaqui de guerra.
Cheguei de manhã ao trabalho e vi que computador estava desligado. Como o Tambaqui é um pequeno servidor também, eu já o deixo ligado. Liguei o peixe e o boot ocorreu normalmente (eu sempre digo que só dá problema o que antes estava funcionando perfeitamente :-), senão ninguém viria com aquela desculpa-esfarrapada-padrão-de-quem-não-tem-backup: ontem estava bonzinho!).Olhando os arquivos depois do boot, vi que haviam vários arquivos com nomes estranhos com ??? e !!!!... sinal de problemas no sistema de arquivos, mas o fsck jurava que estava tudo bem :-)
Rodei então o init 1 para inicializar em usuário único. E lá rodei o fsck no menu do Ubuntu... que detectou vários problemas. Terminada a análise, fiz o boot e para minha surpresa o disco estava vazio !
Como já havia perdido um HD no dia anterior, disse: de novo! :-( Vi que a raiz tinha ido pro saco. Eu precisava apenas de dois diretórios com meus dados, prática que sigo já há alguns anos. Bom, dei uma olhada rápida no disco e constatei que estava mesmo vazio. Eu só consegui dar o boot com o CD do Ubuntu, mas não tive problemas em montar a tal partição. Começei a me preparar para reinstalar o Ubuntu quando vi que a partição não estava vazia.
Eu nunca acreditei muito no lost+found. Toda vez que tive problemas no Linux, encontrei apenas pedaços dos meus arquivos dentro do lost+found. Mas desta vez a coisa estava diferente. Entre estes pedaços estavam diretórios com nomes como #xxxxxx, milhares deles. Logo descobri aonde meus arquivos haviam ido parar.
Iniciei então o seguinte procedimento para procurar meus arquivos:
ls -laR lost+found > bigls.txt
O fsck havia recortado meus diretórios e deixado tudo em um só nível! A opção de gerar o arquivo bigls.txt era de justamente poder procurar com calma tudo que eu poderia salvar. O arquivo final tinha mais de 40MB ! Mas isso não é nada para o less. Utilizando a / para pesquisar, pude navegar no pequeno ls monstro que havia criado e logo detectar o que precisava ser salvo.
Depois, ficou fácil de mover os diretórios mais importantes para um disco de rede. O nome do arquivo eu pegava no bigls.txt, aberto em outra janela com o less. Simples e prático. Ainda estou recuperando as instalações que havia feito na máquina, pois não consegui quase nada do meu /etc (tomcat com ldap é chato).
Nessa nova instalação vou deixar um rsync rodando para o disco de rede... pelo menos dos dados e do /etc :-)
Quanto ao outro disco, rodando Windows XP... no meu velho notebook (2HDs perdidos em 2 meses e contando !)... também consegui montar o disco que nem boot mais tinha usando também o CD do Ubuntu. Ficou tudo muito simples porque o Ubuntu já identifica o disco Windows e é ultra fácil montá-lo, bastando clicar no volume correspondente no menu. Eu gosto muito de usar a linha de comando, mas confesso que gostei desta vez de utilizar as ferramentas gráficas no Ubuntu. Abri dois navegadores, um com a partição Windows e outro com um disco remoto usando Samba. A cópia foi tranqüila, bastando arrastar e soltar! Quem diria um dia fazer isso no Linux :-)
Um programa que ajuda a ver os problemas de boot no Windows é o testdisk. Ele detecta as partições e ajuda a reconstruir o setor de boot entre outras coisas. Pena que ele não está disponível pelo apt-get do Ubuntu 9.04. Mas no link acima tem uma opção para download. Basta baixar e desempacotar a versão binária. Um executável pronto (linkado estaticamente) já está pronto para ser utilizado.
Outro programa para o cinto de utilidades é o SmartMonTools. Ele acessa as informações do S.M.A.R.T. do seu HD e ajuda a decidir o que aconteceu. Claro que é melhor usar essa ferramenta antes de ter problemas, mas meu Windows XP resolveu morrer sem último suspiro. Cheguei de manhã e ele já estava morto, mas sem perda de dados. Nunca se sabe se é um problema de vírus ou simplesmente um defeito no HD que você não havia percebido. Esses utilitários ajudam a executar os testes mesmo post mortem. Claro, dependendo do estado do seu HD, porque se ele nem liga ou nem mesmo é detectado... esqueça.
segunda-feira, 14 de setembro de 2009
COOnan
Sem muito o que fazer, criei um novo personagem no universo dos bárbaros da programação: COOnan. COOnan é um bárbaro que vive em Java... na Indonésia :-D
Como todo bárbaro, poucas ferramentas são tudo que ele tem. Então OO serve pra tudo e não existe qualquer outra coisa no horizonte. Como uma fé inalienável, nada escapa a OO, salvo criação heréticas com nomes mais engraçados que o nome da ilha em que ele vive.
Tudo se resume em aplicações avançadas de OO. Criar uma interface ? Fácil, 5 arquivos e umas 12 classes já fazem o serviço. É classe por classe, objeto por objeto. Métodos gigantes e classes anônimas? não tem problema !
segunda-feira, 7 de setembro de 2009
Rico país pobre
Faz quase 3 anos que estou morando na Bélgica. Um pequeno país europeu, 3 vezes menor que Portugal, um pouco maior que o Estado de Alagoas. Vindo do Brasil, principalmente do Amazonas onde tudo é grande e longe, demora um pouco para entender as distâncias locais.
São pouco mais de 10 milhões da habitantes vivendo na Bélgica. Pra completar, fala-se 3 línguas: holandês, francês e alemão. Onde moro, fala-se francês. O conflito entre o norte (holandês) e o sul (francês) é crescente. A parte que fala alemão é muito pequena para criar problema, eles simplesmente estão lá.
Assisti um vídeo no The Progress Project, sobre o Nokia Data Gathering. O vídeo mostra uma iniciativa muito interessante de usar telefones celulares para coletar informações sobre dengue. Mas o que me chamou a atenção foi lembrar do estado de pobreza que a maioria dos brasilieiros vive. Como falei, moro agora em um pequeno país. O clima é ruim, recursos naturais quase não existem, hoje vive-se em paz, mas antes era cercado por inimigos por todos os lados. O que será que deu errado no nosso Rico País Pobre?
Eu lembro de viver muito bem em Manaus, mas em 2003, meu filho sofreu um "acidente" de carro. Foi levado para um hospital público (João Lúcio) e depois transferido para a Unimed. Uma das coisas que me chamaram atenção é que o hospital público era melhor equipado que a própria Unimed :-( Pior, parecia que o menino era culpado por ter se acidentado e o que a Unimed nos fazia o favor de aceitá-lo, mas isso é coisa passada... O importante é lembrar o estado de necessidades básicas no Brasil: educação, segurança e saúde.
Chegando na Bélgica, minha filha precisou ser hospitalizada. Hospitalizada aqui, porque acho que no Brasil teriam nos mandado para casa. O importante é que o médico decidiu sobre a hospitalização e assim ela foi hospitalizada. Dinheiro veio depois, primeiro a saúde. Considerando que isso aconteceu com um estrangeiro recém chegado e que não falava francês, nem tinha acabado de tirar todos os documentos, um milagre. Mas o caso mais sério estaria por vir, nossa filha Iris, nasceu com lábios leporinos.
Nessa época, já tínhamos todos os documentos, mas não estávamos esperando nada parecido. Ela fez todo os exames pré-natais (detectaram a má formação já no quinto mês de gestação), tivemos acompanhamento psicológico e até apresentaram fotos de bebês com o mesmo problema aos irmãos menores, já prevendo a rejeição que é comum nesses casos. No dia da cirurgia para corrigir os lábios (com 5 dias de vida!), me dirigi ao Hospital e perguntei quanto custaria, porque até então ninguém havia falado nada de dinheiro. A atendente me explicou calmamente: não tenho como dizer antes da cirurgia, o senhor receberá a conta depois de uns 3 meses em sua residência. Eu não sabia se ficava contente ou preocupado com isso. Conta enviada depois de 3 meses?!? Normal aqui, duro para brasileiro acreditar né? Perguntei a ela:
- Quanto custa em média? Eu tenho que me preparar para pagar isso.
Calmamente ela respondeu:
- Não tenho como informar, pois varia com a equipe e com o que acontecer durante a cirurgia. Por que isso é importante para o senhor? Se não puder pagar, o senhor vai deixar de fazer a cirurgia na menina? A questão financeira se discute depois, não se preocupe!
Calei. Já estava me preparando para vender o carro... meu cálculo foi o seguinte: se a visita do encanador custava 65€, imaginei a cirurgia plástica... voltei meu foco para Iris e para minha esposa. Pelo menos a primeira cirurgia estava garantida. Tudo foi realizado com ótimos resultados.
A segunda cirurgia foi realizada 4 meses depois. Essa foi maior e demorou muito mais tempo. Mas os resultados também foram ótimos. Hoje ela é um bebê de quase 2 anos, ainda em tratamento, mas feliz.
O objetivo de relatar esse tipo de experiência é tentar explicar ou entender as enormes diferenças entre nosso país e aqui. Jamais denegrir a imagem do Brasil ou dos médicos brasileiros, mas ressaltar aonde precisamos chegar.
Eu lembro de discussões sobre a riqueza de São Paulo ou de outros Estados brasileiros e me defronto com números de um país gigante, mas ao mesmo tempo pobre e anão. Mesmo São Paulo não é tão rica assim. Em termos de infra-estrutura, a começar pelo metrô, fica fácil de perceber as diferenças entre uma grande cidade rica (Paris) e uma grande cidade pobre (São Paulo). Com menos de 30 milhões de automóveis para quase 200 milhões de habitantes e um sistema de transporte precário, fica óbvio que a coisa não é tão rosa como alguns tentam se convencer. Claro, lendo isso na tela do computador já lhe separa da grande maioria dos brasileiros.
Mas como disse, já estou aqui há quase três anos e não é uma questão de competência pessoal ou resultado de um governo incorruptível (que nem aqui existe). O que será então a grande diferença entre estes dois países?
Minha opinião, humilde uma vez que não realizei um grande estudo, apenas observações do dia a dia; é que a grande diferença é no papel do estado em prover o básico para a formação de uma sociedade sadia. Já falei de saúde, o Brasil tem feito progressos, mas ainda está muito atrasado: os pobres sofrem nas mãos do atendimento público, sub-financiado e os ricos são reféns de planos de saúde, mas que resolvem os problemas mais básicos. Isso impede a melhoria de vida e do atendimento aos pobres, porque ninguém liga pros pobres, eles vivem em outro mundo, não é? Salvo em época de eleição.
Educação é outro ponto. Universidades públicas excelentes, mas escolas primárias e secundárias em tempo parcial. Não que não haja melhorias, mas na educação também temos dois sistemas paralelos: os ricos em escolas particulares e os pobres em escolas públicas. Quem estuda no grupo é o filho da empregada doméstica, então ninguém liga :-(
Segurança nem se fala. Olhando os índices de violência por bairro (fiz isso com os de Manaus em 2005) fica claro que os bairros mais violentos são os mais pobres. Isso fica pior em cidades onde os pobres são verdadeiramente excluídos, vivendo o mais longe melhor. Nos bairros ricos, os crimes também acontecem e são esses os combatidos e mostrados na TV!
Não é questão de ricos contra pobres, mas a existência de realidades paralelas no mesmo país. Se o sistema de saúde fosse realmente único, se todos tivessem que esperar o mesmo para ter uma consulta médica ou estudar nas mesmas escolas, a coisa seria diferente. Aí nos importaríamos realmente no que acontece no outro mundo. Pararíamos de enxergar os ricos como ladrões e os pobres como coitados... ou seria o inverso?
Isso tudo se resume em diminuir a desigualdade social. Na Europa, os ricos são muito ricos, mas também são muito poucos. Um engenheiro ganha de duas a três vezes mais que um operário e dependendo do país essa diferença é ainda menor! Nada como a diferença de 10 ou 20 vezes mais entre o salário de um engenheiro e o valor do salário mínimo. Depois não se entende o por quê da desigualdade !
Rico mora em condomínio fechado com segurança privada e faz compra em shopping centers... o problema de segurança é mais sério no trajeto entre estes pontos e o trabalho, mas é tolerado. Criamos dois mundos no mesmo país, uma fronteira imaginária entre ricos e pobres.
O que fazer? O que é pior, ser rico no país dos pobres ou pobre no país dos ricos? Temos que escolher e pensar no bem de todos. Violência se resolve com renda e emprego, mas as pessoas precisam de saúde, moradia e educação para chegar lá. Eu sempre achei programas como o bolsa família populistas e criado por políticos na época de eleição, mas hoje acredito ser o caminho para obtermos um equilíbrio social durante a formação da nossa sociedade. Hoje o valor do bolsa família ainda é muito baixo, sentiríamos mais resultados se o valor fosse próximo ao do salário mínimo. Não é tão utópico assim, é comum na Europa. Na Bélgica se chama alocação familiar e é pago por filho com idade entre 0 e 24 anos (se continuar estudando). Embora não tenha o nome de bolsa família é bem parecido. Na época de volta às aulas, depositam uns 50€ para ajudar nos gastos (aqui 150€ de material escolar é um escândalo e sai na TV... ai eu pagava muito mais que isso, uns R$1.000 a R$1.500!). Recebe-se 800€ de prêmio pelo nascimento (1200€ pro primeiro filho)... Até sair da faculdade, valores entre 95 e 200€ por mês/filho são depositados na conta da esposa (pro pai não comprar pinga!).
Temos um grande complexo de inferioridade como povo. Coisas como europeus são bons (fazendo referência aos brancos que nascem no Brasil) e todos os outros são vagabundos, corruptos ou simplesmente desajustados. Isso é um forma deslavada de racismo, mas que contamina nossa sociedade. É aceitar o estigma do jeitinho brasilieiro como algo gravado em nosso DNA. Alias, aqui, Brasil é tudo igual. Europeu é quem tem passaporte europeu e não importa a cor da pele.
Sabendo que não são somos predestinados ao fracasso como povo e que nem negros, portugueses ou índios tem qualquer coisa a ver com isso... o que nos falta? Assumir a responsabilidade pelos nossos atos e omissões e continuar a vida como agentes de mudança e não como vítimas da história.
Eu realmente acredito que o Brasil será um grande país num futuro próximo, pensando em 2050. As taxas de crescimento e educação vem melhorando, lentamente, mas continuamente. É preciso acreditar e identificar o que está realmente errado. Iniciativas como a Associação Amada, responsável pela divulgação do implante coclear em Manaus, mostram como chegar lá e o que fazer no caminho. É quando nossa sociedade se organizar para fortalecer suas bases e não apenas fazer carnaval (nada contra a festa, mas imagine um grande mutirão da educação ou barracões de matemática e ciências!). Feliz 7 de Setembro, dia da independência do Brasil.
domingo, 6 de setembro de 2009
Fotos panorâmicas com Hugin
Tô começando a gostar de fotografia de verdade. Esta fotografia foi criada com o software livre Hugin. Juntando-se 4 fotos normais, obtêm-se a vista panorâmica. Muito fácil de usar!
Assinar:
Postagens (Atom)