středa 24. listopadu 2004

Ant a malá "zrada" kompilace

Tento příspěvek mám v hlavě již delší dobu, jenom nebyl čas se podělit o zajímavou true story. Bylo nebylo, Hloupý Honza vyrazil na instalaci aplikačního serveru spolu s nejnovější verzí softwaru společnosti Pohádka s.r.o. Jelikož byl Honza hloupý a nikoliv líný vše si pečlivě připravil. Úhledně připravený distribuční balíček, byl vytvořen z posledního stavu v CVS, pomocí mravenečka jménem Ant.

Posílen buchtami od maminky, vyrazil na cestu do království za sedmero řekami a horami. Honzův úkol v podobě nainstalování se povedl na výbornou. Drak na obloze objevil se při testování funkčnosti. Marně sváděl udatný bojovník souboj s Drakem, ostrý meč v podobě aktuálních zdrojů jenž platil na cvičišti nepomáhal. S nepořízenou se domů Honzík vrátil. Zrada se ukázala druhý den, ten mrzký sluha Ant byl vinen, on zradil pána svého.

Po této pohádkové vložce se přesuneme k problému. Máme třídu Foo a třídu Constant. Třída Constant obsahuje pouze konstanty, které využivá třída Foo.

 
public class Constant {
    public static final String SOMETHING_FISHY = "Something fishy";
}

public class Foo {
    public static void main(String args[]){
        System.out.println(Constant.SOMETHING_FISHY);
    }
}
 

Pro kompilaci máme antovský task.

 
<target name="build">
        <mkdir dir="${build.dir}"/>    	
        <javac 	destdir="${build.dir}" 
        		target="1.4" 
        		debug="true" 
        		deprecation="false" 
        		optimize="false" 
        		failonerror="true"
        >
            <src path="${src.dir}"/>            
        </javac>   	
</target>
 

Build task pustíme a necháme si zdrojové kódy zkompilovat. Zkompilovaný prográmek pustíme

 
java Foo

Something fishy
 

Výsledek zcela očekávaný. Uděláme malou úpravu zdrojového kódu a hodnotu konstanty SOMETHING_FISHY změníme.

 
public class Constant {
    public static final String SOMETHING_FISHY = "Kim Philby";
}
 

Znovu pustíme build task a zkompilovaný prográmek.

 
java Foo

Something fishy
 

Místo očekávaného Kima Philbyho máme na výstupu Something fishy, kde je chyba? Bohužel to není chyba, ale je to vlastnost. Ant se rozhodne třídu zkompilovat pokud nemá odpovídající class soubor a nebo pokud je class soubor starší než třída resp. zdrojový soubor. To v našem případě sice platí, ale kompilátor z důvodu optimalizace přikompiluje obsah konstanty do třídy Foo. O tom se můžeme přesvědčit dekompilací.

 
// Decompiled by DJ v3.5.5.77 
// Copyright 2003 Atanas Neshkov  Date: 24.11.2004 8:22:41
// Home Page : http://members.fortunecity.com/neshkov/dj.html  
// - Check often for new version!
// Decompiler options: packimports(3) 
// Source File Name:   Foo.java

import java.io.PrintStream;

public class Foo
{

    public Foo()
    {
    }

    public static void main(String args[])
    {
        System.out.println("Something fishy");
    }
}
  

Spásou by mohl být antovský depend task, žel bohu na konci jeho popisu najdeme v sekci Limitations následující odstaveček

The most obvious example of these limitations is that the task can't tell which classes to recompile when a constant primitive data type exported by other classes is changed. For example, a change in the definition of something like

 
public final class Constants {
  public final static boolean DEBUG=false;
}
 

will not be picked up by other classes.

Co dodat závěrem kromě toho, že mě alias Hloupého Honzu to pěkně vypeklo? Snad jen radu, přidejte si na první místo build war tasku smazání cílového adresáře pro classes. Pokud si chcete ověřit chování, zdrojové kódy jsou k dispozici.