Notice: This Wiki is now read only and edits are no longer possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.
Difference between revisions of "SWT/Developer Guide"
(→eclipse.platform.swt/bundles/org.eclipse.swt/Eclipse SWT PI) |
(→eclipse.platform.swt.binaries: Add link to where native are built) |
||
(18 intermediate revisions by the same user not shown) | |||
Line 42: | Line 42: | ||
* [http://git.eclipse.org/c/platform/eclipse.platform.swt.git eclipse.platform.swt], the main repository with the source code | * [http://git.eclipse.org/c/platform/eclipse.platform.swt.git eclipse.platform.swt], the main repository with the source code | ||
− | * [http://git.eclipse.org/c/platform/eclipse.platform.swt.binaries.git/ eclipse.platform.swt.binaries], used mainly for technical reasons to build [https://wiki.eclipse.org/FAQ_What_is_a_plug-in_fragment%3F fragments] ([http://wiki.osgi.org/wiki/Fragment OSGi's definition]) and store binary versions of the native libraries. See | + | * [http://git.eclipse.org/c/platform/eclipse.platform.swt.binaries.git/ eclipse.platform.swt.binaries], used mainly for technical reasons to build [https://wiki.eclipse.org/FAQ_What_is_a_plug-in_fragment%3F fragments] ([http://wiki.osgi.org/wiki/Fragment OSGi's definition]) and store binary versions of the native libraries. See [https://www.eclipse.org/swt/git.php documentation about using Git with SWT]. |
=== eclipse.platform.swt === | === eclipse.platform.swt === | ||
Line 63: | Line 63: | ||
* '''gtk''' — Linux GTK | * '''gtk''' — Linux GTK | ||
* '''win32''' — Windows Win32 | * '''win32''' — Windows Win32 | ||
− | |||
It means that for compiling DnD support on OS X with Cocoa, we need the sources in <code>cocoa</code> and <code>common</code> but for Windows, I need <code>win32</code> and <code>common</code>. For platform without native DnD support both <code>common</code> and <code>emulated</code> are needed. | It means that for compiling DnD support on OS X with Cocoa, we need the sources in <code>cocoa</code> and <code>common</code> but for Windows, I need <code>win32</code> and <code>common</code>. For platform without native DnD support both <code>common</code> and <code>emulated</code> are needed. | ||
Line 72: | Line 71: | ||
* <code>.classpath_cocoa</code> — OS X | * <code>.classpath_cocoa</code> — OS X | ||
− | |||
* <code>.classpath_gtk</code> — Linux GTK | * <code>.classpath_gtk</code> — Linux GTK | ||
− | * <code>. | + | * <code>.classpath_gtk_win32</code> — Windows GTK |
* <code>.classpath_win32</code> — Windows Win32 | * <code>.classpath_win32</code> — Windows Win32 | ||
− | |||
− | |||
− | |||
− | So the first thing you have to do is to copy the classpath file that suit your environment to a file named <code>.classpath</code> (or create a symbolic link). | + | So the first thing you have to do is to copy the classpath file that suit your environment to a file named <code>.classpath</code> (or create a symbolic link). |
=== eclipse.platform.swt/bundles/org.eclipse.swt/Eclipse SWT === | === eclipse.platform.swt/bundles/org.eclipse.swt/Eclipse SWT === | ||
Line 86: | Line 81: | ||
In sub-platform-specific-source folders, you will find all the core classes of SWT (widgets, graphics, event...). Except in <code>common</code> subfolder, there is no native code in here. There is only Java code. This code calls classes in other subfolders (mainly ''Eclipse SWT PI'') which contains native methods (like <code>org.eclipse.swt.internal.XXX.OS</code> or <code>org.eclipse.swt.internal.C</code>). | In sub-platform-specific-source folders, you will find all the core classes of SWT (widgets, graphics, event...). Except in <code>common</code> subfolder, there is no native code in here. There is only Java code. This code calls classes in other subfolders (mainly ''Eclipse SWT PI'') which contains native methods (like <code>org.eclipse.swt.internal.XXX.OS</code> or <code>org.eclipse.swt.internal.C</code>). | ||
− | In the <code>common/library | + | In the <code>common/library/common</code> source folder, you will find two native libraries: <code>swt</code> and <code>callback</code>. <code>swt</code> is hand written and contains global macro declarations for SWT. <code>callback</code> is a hand written JNI library for the class <code>org.eclipse.swt.internal.Callback</code>. It is one of the few library which is not written for a specific OS or WS and is thus portable. |
=== eclipse.platform.swt/bundles/org.eclipse.swt/Eclipse SWT PI === | === eclipse.platform.swt/bundles/org.eclipse.swt/Eclipse SWT PI === | ||
− | + | {{tip||'''PI''' stands for '''Platform Interface'''}} | |
− | + | ||
This folder contains source folder with OS/WS specific codes; meaning it contains Java classes with "native" methods but also the corresponding C code. The exception to that is again the folders ''common'' and ''common_j2se''. ''common'' contains Java and native code for portable C functions (e.g. malloc, free, memmove). This code lies in <code>C.java</code> and the associated library. ''common_j2se'' (and ''common_j2me'' for some platforms) contains a single Java class <code>Library</code> which is responsible for loading the compiled native libraries. Basically it calls <code>System.loadLibrary(String)</code> with a lot of checkings around the call. | This folder contains source folder with OS/WS specific codes; meaning it contains Java classes with "native" methods but also the corresponding C code. The exception to that is again the folders ''common'' and ''common_j2se''. ''common'' contains Java and native code for portable C functions (e.g. malloc, free, memmove). This code lies in <code>C.java</code> and the associated library. ''common_j2se'' (and ''common_j2me'' for some platforms) contains a single Java class <code>Library</code> which is responsible for loading the compiled native libraries. Basically it calls <code>System.loadLibrary(String)</code> with a lot of checkings around the call. | ||
Line 98: | Line 92: | ||
=== eclipse.platform.swt.binaries === | === eclipse.platform.swt.binaries === | ||
− | This repository contains only one folder: ''bundles'' which contains one folder/project per supported combination of <abbr title="Windowing System">WS</abbr>/<abbr title="Operating System">OS</abbr>/<abbr title="CPU Architecture">ARCH</abbr> | + | This repository contains only one folder: ''bundles'' which contains one folder/project per supported combination of <abbr title="Windowing System">WS</abbr>/<abbr title="Operating System">OS</abbr>/<abbr title="CPU Architecture">ARCH</abbr>. These bundles are actually OSGi fragments. They don't hold any code, they serve as placeholder where all the sources relative to the platform under build are copied. It also holds a pre-compiled version of the binaries. For legacy reasons, the <code>.so</code>, <code>.jnilib</code> or <code>.dll</code> files are committed at the root of these project when they are built (by [https://ci.eclipse.org/releng/view/SWT%20Natives%20(master)/ the releng CI instance]). |
== JNI Generator == | == JNI Generator == | ||
− | SWT uses a home made tool call JNI Generator to generate both <code>.c</code> and <code>.h</code> files. The source of this tool are located in the project [http://git.eclipse.org/c/platform/eclipse.platform.swt.git/tree/bundles/org.eclipse.swt.tools/JNI%20Generation org.eclipse.swt.tools (JNI Generation subfolder)]. A introductory documentation of this tool is [https://www.eclipse.org/swt/jnigen.php available online]. Basically, it parses the file OS.java in the current classpath and generates os.c and os.h accordingly. Whenever you change something in OS.java in your IDE, a dedicated builder (org.eclipse.swt.tools.jnibuilder) is called along with the Java compilation to update os.c and os.h. | + | SWT uses a home made tool call JNI Generator to generate both <code>.c</code> and <code>.h</code> files. The source of this tool are located in the project [http://git.eclipse.org/c/platform/eclipse.platform.swt.git/tree/bundles/org.eclipse.swt.tools/JNI%20Generation org.eclipse.swt.tools (JNI Generation subfolder)]. A introductory documentation of this tool is [https://www.eclipse.org/swt/jnigen.php available online]. Basically, it parses the file OS.java in the current classpath and generates os.c and os.h accordingly. Whenever you change something in OS.java in your IDE, a dedicated builder (<code>org.eclipse.swt.tools.jnibuilder</code>) is called along with the Java compilation to update os.c and os.h. |
+ | |||
+ | === Mac Generator === | ||
+ | |||
+ | In the case of OS X, even <code>OS.java</code> is generated. To do so, use the [https://www.eclipse.org/swt/macgen.php MacGen app] | ||
+ | |||
+ | === JNI Flags === | ||
+ | |||
+ | This documentation has been extracted from the SWT Tools templates | ||
+ | |||
+ | {| class="wikitable" | ||
+ | !scope="col"|Flag | ||
+ | !scope="col"|Class | ||
+ | !scope="col"|Field | ||
+ | !scope="col"|Method | ||
+ | !scope="col"|Param | ||
+ | !scope="col"|Description | ||
+ | |- | ||
+ | !scope="row"|no_gen | ||
+ | |style="text-align:center;"|X | ||
+ | |style="text-align:center;"|X | ||
+ | | | ||
+ | | | ||
+ | |Wether to generate the JNI element or not | ||
+ | |- | ||
+ | !scope="row"|cpp | ||
+ | |style="text-align:center;"|X | ||
+ | | | ||
+ | |style="text-align:center;"|X | ||
+ | | | ||
+ | | | ||
+ | |- | ||
+ | !scope="row"|no_wince | ||
+ | | | ||
+ | |style="text-align:center;"|X | ||
+ | | | ||
+ | | | ||
+ | | | ||
+ | |- | ||
+ | !scope="row"|address | ||
+ | | | ||
+ | | | ||
+ | |style="text-align:center;"|X | ||
+ | | | ||
+ | | | ||
+ | |- | ||
+ | !scope="row"|const | ||
+ | | | ||
+ | | | ||
+ | |style="text-align:center;"|X | ||
+ | | | ||
+ | | | ||
+ | |- | ||
+ | !scope="row"|const address | ||
+ | | | ||
+ | | | ||
+ | |style="text-align:center;"|X | ||
+ | | | ||
+ | | | ||
+ | |- | ||
+ | !scope="row"|dynamic | ||
+ | | | ||
+ | | | ||
+ | |style="text-align:center;"|X | ||
+ | | | ||
+ | | | ||
+ | |- | ||
+ | !scope="row"|jni | ||
+ | | | ||
+ | | | ||
+ | |style="text-align:center;"|X | ||
+ | | | ||
+ | | | ||
+ | |- | ||
+ | !scope="row"|cast | ||
+ | | | ||
+ | | | ||
+ | |style="text-align:center;"|X | ||
+ | | | ||
+ | | | ||
+ | |- | ||
+ | !scope="row"|no_gen cpp | ||
+ | | | ||
+ | | | ||
+ | |style="text-align:center;"|X | ||
+ | | | ||
+ | | | ||
+ | |- | ||
+ | !scope="row"|new | ||
+ | | | ||
+ | | | ||
+ | |style="text-align:center;"|X | ||
+ | | | ||
+ | | | ||
+ | |- | ||
+ | !scope="row"|no_gen new | ||
+ | | | ||
+ | | | ||
+ | |style="text-align:center;"|X | ||
+ | | | ||
+ | | | ||
+ | |- | ||
+ | !scope="row"|delete | ||
+ | | | ||
+ | | | ||
+ | |style="text-align:center;"|X | ||
+ | | | ||
+ | | | ||
+ | |- | ||
+ | !scope="row"|gcnew | ||
+ | | | ||
+ | | | ||
+ | |style="text-align:center;"|X | ||
+ | | | ||
+ | | | ||
+ | |- | ||
+ | !scope="row"|gcobject | ||
+ | | | ||
+ | | | ||
+ | |style="text-align:center;"|X | ||
+ | | | ||
+ | | | ||
+ | |- | ||
+ | !scope="row"|setter | ||
+ | | | ||
+ | | | ||
+ | |style="text-align:center;"|X | ||
+ | | | ||
+ | | | ||
+ | |- | ||
+ | !scope="row"|getter | ||
+ | | | ||
+ | | | ||
+ | |style="text-align:center;"|X | ||
+ | | | ||
+ | | | ||
+ | |- | ||
+ | !scope="row"|adder | ||
+ | | | ||
+ | | | ||
+ | |style="text-align:center;"|X | ||
+ | | | ||
+ | | | ||
+ | |- | ||
+ | !scope="row"|trycatch | ||
+ | | | ||
+ | | | ||
+ | |style="text-align:center;"|X | ||
+ | | | ||
+ | | | ||
+ | |- | ||
+ | !scope="row"|no_in | ||
+ | | | ||
+ | | | ||
+ | | | ||
+ | |style="text-align:center;"|X | ||
+ | | | ||
+ | |- | ||
+ | !scope="row"|no_out | ||
+ | | | ||
+ | | | ||
+ | | | ||
+ | |style="text-align:center;"|X | ||
+ | | | ||
+ | |- | ||
+ | !scope="row"|critical | ||
+ | | | ||
+ | | | ||
+ | | | ||
+ | |style="text-align:center;"|X | ||
+ | | | ||
+ | |- | ||
+ | !scope="row"|init | ||
+ | | | ||
+ | | | ||
+ | | | ||
+ | |style="text-align:center;"|X | ||
+ | | | ||
+ | |- | ||
+ | !scope="row"|struct | ||
+ | | | ||
+ | | | ||
+ | | | ||
+ | |style="text-align:center;"|X | ||
+ | | | ||
+ | |- | ||
+ | !scope="row"|unicode | ||
+ | | | ||
+ | | | ||
+ | | | ||
+ | |style="text-align:center;"|X | ||
+ | | | ||
+ | |- | ||
+ | !scope="row"|sentinel | ||
+ | | | ||
+ | | | ||
+ | | | ||
+ | |style="text-align:center;"|X | ||
+ | | | ||
+ | |- | ||
+ | !scope="row"|gcobject | ||
+ | | | ||
+ | | | ||
+ | | | ||
+ | |style="text-align:center;"|X | ||
+ | | | ||
+ | |- | ||
+ | !scope="row"|no_in critical | ||
+ | | | ||
+ | | | ||
+ | | | ||
+ | |style="text-align:center;"|X | ||
+ | | | ||
+ | |- | ||
+ | !scope="row"|no_out critical | ||
+ | | | ||
+ | | | ||
+ | | | ||
+ | |style="text-align:center;"|X | ||
+ | | | ||
+ | |} | ||
+ | |||
+ | == Build SWT == | ||
+ | |||
+ | 2 modes | ||
+ | |||
+ | === Build SWT.jar for standalone SWT app === | ||
+ | |||
+ | * Clone both SWT's Git repositories side by side. | ||
+ | * Go to org.eclipse.swt.WS.OS.ARCH which fits your current platform. | ||
+ | * Run Ant on the <code>build.xml</code> file on the target <code>build.jars</code> (e.g. <code>ant build.jars</code>). This will '''not''' build the native code. It will only compile Java code. You will need to use the pre-compiled native libraries if you use this method. | ||
+ | * You will get a swt.jar file at the root of org.eclipse.swt.WS.OS.ARCH. This file is a bare JAR file, not an OSGi bundle. It does not contain the native libraries required to run SWT properly. | ||
+ | |||
+ | {{note||[https://www.eclipse.org/swt/faq.php#howbuildjar SWT's FAQ] also explains how to build this jar. It only does not state that no natives are build, and that the result is not an OSGi bundle.}} | ||
+ | === Build org.eclipse.swt plugin along with the appropriate fragment for SWT/OSGi app (e.g. RCP) === | ||
+ | |||
+ | === Requirements === | ||
+ | |||
+ | ==== Windows (8.1) ==== | ||
+ | |||
+ | ==== Linux GTK (on CentOS) ==== | ||
+ | |||
+ | gcc cairo-devel gtk3-devel webkit2gtk3-devel mesa-libGLU-devel libXt-devel | ||
+ | |||
+ | ==== OS X (10.10 - Yosemite) ==== |
Latest revision as of 11:11, 17 August 2022
Introduction
SWT uses native operating systems widget toolkits. The most common supported operating system (or OS) supported by SWT Windows, Linux and OS X. The most common native toolkits (aka windowing system or WS) are: Win32 on Windows, GTK on Linux and Cocoa on OS X. Finally, SWT runs on several CPU architectures (or ARCH), the two most common being x86 (also known as i386, IA-32 or i586) and x86_64 (also known as amd64 or x64).
SWT is a Java framework. In order to be able to use the native toolkits, SWT has to use a feature of Java to call native code. This feature is called Java Native Interface (JNI). JNI lets you define methods in Java with the keyword native and the behavior of this method will be delegated to some native code (C/C++ or Objective C most of the time).
Usually with JNI, you would have to write the native code manually, and run the javah
command on the Java files with native method to generate header that would bind your Java code and the native code (before compiling it). You would end up with something like that:
-
HelloWorld.java
with a method declared as "native". This class is compiled withjavac
to a.class
file. -
HelloWorld.h
is a C header file with the signature of the native method. It can be generated by the programjavah
(available with the Java SDK). -
libHelloWorld.c
, the native C code using system calls and JNI data structures to manipulate Java objects. This file requires to#include HelloWorld.h
.
This method has several drawbacks. First, writing native code is very error prone. It is especially the case with SWT because it is only a very thin layer between Java and the native toolkit.
Basically, SWT is one-to-one mapping between a native Java method a a native system call. Lets take the gdk_window_set_cursor
GTK system call. In the file OS.java
, SWT developers write
public static final native void gdk_window_set_cursor(long window, long cursor);
and in the file os.c
JNIEXPORT void JNICALL gdk_window_set_cursor (JNIEnv *env, jclass that, jintLong arg0, jintLong arg1) { gdk_window_set_cursor((GdkWindow *)arg0, (GdkCursor *)arg1); }
This is pretty straightforward but cumbersome (and error prone) to write manually.
A second drawback about using javah
is that it does not handle 32/64 bits automatically. You have to duplicate your code if you want to handle both of them. The overhead of writing both versions is heavyweight so SWT tackles the two drawbacks at once. You will read more about how it is done later, but first, lets dive inside the source code tree organization.
Source tree organization
SWT is developed in two Git repositories:
- eclipse.platform.swt, the main repository with the source code
- eclipse.platform.swt.binaries, used mainly for technical reasons to build fragments (OSGi's definition) and store binary versions of the native libraries. See documentation about using Git with SWT.
eclipse.platform.swt
This repository is organized around five root folders
- bundles — where the code actually is written. It contains a project with the code of the SWT Tools (org.eclipse.swt.tools), a project org.eclipse.swt.fragments.localbuild (??? Don't know what it is about) and org.eclipse.swt the main project.
- features — contains only one feature: SWT Tools. It is used to be able to install all SWT Tools (JNI Generator, Sleak and SWT Spy) in the IDE at once.
- local-build — HELP NEEDED
- examples — code samples and snippets
- tests — JUnit and performance tests.
eclipse.platform.swt/bundles/org.eclipse.swt
It is a Java (JDT) project. It has a lot of source folders (between 25 and 30 depending on the platform you are building). The source code is split by features: printing support, browser support, OpenGL... Within each source folder, the code is organized by WS (win32, cocoa, gtk...). When some code does not require native call, it is written in pure Java and located in a folder named common (sometimes common_j2se or common_j2me when the WS is supported on mobile devices where only J2ME can be run). Let's take an example with the folder Eclipse SWT Drag and Drop (DnD). It contains 6 subfolders:
- cocoa — OS X
- common — Code common to all platforms (Java only)
- emulated — Emulation of some features for platforms without native support (Java only)
- gtk — Linux GTK
- win32 — Windows Win32
It means that for compiling DnD support on OS X with Cocoa, we need the sources in cocoa
and common
but for Windows, I need win32
and common
. For platform without native DnD support both common
and emulated
are needed.
Fortunately, you don't have to know the proper combination of source folders you need for your platform. org.eclipse.swt
project has several .classpath
files that defines all the source folders to take into account for each combination of WS/OS/ARCH:
-
.classpath_cocoa
— OS X -
.classpath_gtk
— Linux GTK -
.classpath_gtk_win32
— Windows GTK -
.classpath_win32
— Windows Win32
So the first thing you have to do is to copy the classpath file that suit your environment to a file named .classpath
(or create a symbolic link).
eclipse.platform.swt/bundles/org.eclipse.swt/Eclipse SWT
In sub-platform-specific-source folders, you will find all the core classes of SWT (widgets, graphics, event...). Except in common
subfolder, there is no native code in here. There is only Java code. This code calls classes in other subfolders (mainly Eclipse SWT PI) which contains native methods (like org.eclipse.swt.internal.XXX.OS
or org.eclipse.swt.internal.C
).
In the common/library/common
source folder, you will find two native libraries: swt
and callback
. swt
is hand written and contains global macro declarations for SWT. callback
is a hand written JNI library for the class org.eclipse.swt.internal.Callback
. It is one of the few library which is not written for a specific OS or WS and is thus portable.
eclipse.platform.swt/bundles/org.eclipse.swt/Eclipse SWT PI
This folder contains source folder with OS/WS specific codes; meaning it contains Java classes with "native" methods but also the corresponding C code. The exception to that is again the folders common and common_j2se. common contains Java and native code for portable C functions (e.g. malloc, free, memmove). This code lies in C.java
and the associated library. common_j2se (and common_j2me for some platforms) contains a single Java class Library
which is responsible for loading the compiled native libraries. Basically it calls System.loadLibrary(String)
with a lot of checkings around the call.
Then you have the OS/WS specific source folder. It contains org.eclipse.swt.internal.Lock
and org.eclipse.swt.internal.Platform
. Lock is a pure Java class but its implementation may differ from an platform to another. Platform is a holder for the platform name constant PLATFORM
and the static lock singleton. Note, that C
class extends Platform
. The most important in this source folder is the org.eclipse.swt.internal.XXX
where XXX is the name of the platform. It contains all the Java code specific to the current platform. In this package there is a class even more important. It is called OS.java
on all platforms despite it is not in the same package for all platforms. This si where all the method of the one-to-one mapping is done. It means that every native in the class OS
correspond to a single native system call. The corresponding library os.c
and os.h
are generated by the JNI Generator.
eclipse.platform.swt.binaries
This repository contains only one folder: bundles which contains one folder/project per supported combination of WS/OS/ARCH. These bundles are actually OSGi fragments. They don't hold any code, they serve as placeholder where all the sources relative to the platform under build are copied. It also holds a pre-compiled version of the binaries. For legacy reasons, the .so
, .jnilib
or .dll
files are committed at the root of these project when they are built (by the releng CI instance).
JNI Generator
SWT uses a home made tool call JNI Generator to generate both .c
and .h
files. The source of this tool are located in the project org.eclipse.swt.tools (JNI Generation subfolder). A introductory documentation of this tool is available online. Basically, it parses the file OS.java in the current classpath and generates os.c and os.h accordingly. Whenever you change something in OS.java in your IDE, a dedicated builder (org.eclipse.swt.tools.jnibuilder
) is called along with the Java compilation to update os.c and os.h.
Mac Generator
In the case of OS X, even OS.java
is generated. To do so, use the MacGen app
JNI Flags
This documentation has been extracted from the SWT Tools templates
Flag | Class | Field | Method | Param | Description |
---|---|---|---|---|---|
no_gen | X | X | Wether to generate the JNI element or not | ||
cpp | X | X | |||
no_wince | X | ||||
address | X | ||||
const | X | ||||
const address | X | ||||
dynamic | X | ||||
jni | X | ||||
cast | X | ||||
no_gen cpp | X | ||||
new | X | ||||
no_gen new | X | ||||
delete | X | ||||
gcnew | X | ||||
gcobject | X | ||||
setter | X | ||||
getter | X | ||||
adder | X | ||||
trycatch | X | ||||
no_in | X | ||||
no_out | X | ||||
critical | X | ||||
init | X | ||||
struct | X | ||||
unicode | X | ||||
sentinel | X | ||||
gcobject | X | ||||
no_in critical | X | ||||
no_out critical | X |
Build SWT
2 modes
Build SWT.jar for standalone SWT app
- Clone both SWT's Git repositories side by side.
- Go to org.eclipse.swt.WS.OS.ARCH which fits your current platform.
- Run Ant on the
build.xml
file on the targetbuild.jars
(e.g.ant build.jars
). This will not build the native code. It will only compile Java code. You will need to use the pre-compiled native libraries if you use this method. - You will get a swt.jar file at the root of org.eclipse.swt.WS.OS.ARCH. This file is a bare JAR file, not an OSGi bundle. It does not contain the native libraries required to run SWT properly.
Build org.eclipse.swt plugin along with the appropriate fragment for SWT/OSGi app (e.g. RCP)
Requirements
Windows (8.1)
Linux GTK (on CentOS)
gcc cairo-devel gtk3-devel webkit2gtk3-devel mesa-libGLU-devel libXt-devel