diff options
-rw-r--r-- | .travis.yml | 27 | ||||
-rwxr-xr-x | other/travis/freebsd-install | 176 | ||||
-rwxr-xr-x | other/travis/freebsd-install-stage1 | 165 | ||||
-rwxr-xr-x | other/travis/freebsd-install-stage2 | 32 | ||||
-rwxr-xr-x | other/travis/freebsd-script-stage1 | 4 | ||||
-rwxr-xr-x | other/travis/phase | 10 |
6 files changed, 231 insertions, 183 deletions
diff --git a/.travis.yml b/.travis.yml index ef70efb3..7c96fb30 100644 --- a/.travis.yml +++ b/.travis.yml | |||
@@ -2,21 +2,34 @@ language: c | |||
2 | 2 | ||
3 | matrix: | 3 | matrix: |
4 | include: | 4 | include: |
5 | - env: JOB=toxcore ENV=osx RUN_TESTS=true | 5 | - stage: "Stage 1" |
6 | os: osx | 6 | env: JOB=autotools ENV=linux |
7 | - env: JOB=autotools ENV=linux | ||
8 | compiler: gcc | 7 | compiler: gcc |
9 | - env: JOB=toxcore ENV=linux | 8 | - stage: "Stage 1" |
9 | env: JOB=toxcore ENV=linux | ||
10 | compiler: clang | 10 | compiler: clang |
11 | - env: JOB=toxcore ENV=windows ARCH=i686 | 11 | - stage: "Stage 1" |
12 | env: JOB=toxcore ENV=windows ARCH=i686 | ||
12 | services: | 13 | services: |
13 | - docker | 14 | - docker |
14 | - env: JOB=toxcore ENV=windows ARCH=x86_64 | 15 | - stage: "Stage 1" |
16 | env: JOB=toxcore ENV=windows ARCH=x86_64 | ||
15 | services: | 17 | services: |
16 | - docker | 18 | - docker |
17 | - env: JOB=toxcore ENV=freebsd | 19 | - stage: "Stage 1" |
20 | env: JOB=toxcore ENV=freebsd | ||
21 | dist: trusty | ||
22 | sudo: required | ||
23 | install: other/travis/phase $JOB $ENV install stage1 | ||
24 | script: other/travis/phase $JOB $ENV script stage1 | ||
25 | - stage: "Stage 2" | ||
26 | env: JOB=toxcore ENV=freebsd | ||
18 | dist: trusty | 27 | dist: trusty |
19 | sudo: required | 28 | sudo: required |
29 | install: other/travis/phase $JOB $ENV install stage2 | ||
30 | - stage: "Stage 2" | ||
31 | env: JOB=toxcore ENV=osx RUN_TESTS=true | ||
32 | os: osx | ||
20 | fast_finish: true | 33 | fast_finish: true |
21 | 34 | ||
22 | addons: | 35 | addons: |
diff --git a/other/travis/freebsd-install b/other/travis/freebsd-install index cf56d989..d2f0164b 100755 --- a/other/travis/freebsd-install +++ b/other/travis/freebsd-install | |||
@@ -23,8 +23,6 @@ | |||
23 | 23 | ||
24 | sudo apt-get install -y qemu | 24 | sudo apt-get install -y qemu |
25 | 25 | ||
26 | git tag -l --sort=version:refname > GIT_TAGS | ||
27 | |||
28 | OLD_PWD="$PWD" | 26 | OLD_PWD="$PWD" |
29 | 27 | ||
30 | mkdir -p /opt/freebsd/cache | 28 | mkdir -p /opt/freebsd/cache |
@@ -86,178 +84,4 @@ stop_vm() | |||
86 | # Let's see what's in the cache directory | 84 | # Let's see what's in the cache directory |
87 | ls -lh | 85 | ls -lh |
88 | 86 | ||
89 | # === Get the VM running, configured to run ssh server and updated === | ||
90 | |||
91 | # Create image if it's not cached | ||
92 | if [ ! -f ./$IMAGE_NAME.tgz ]; then | ||
93 | |||
94 | rm -rf ./* | ||
95 | |||
96 | # https://download.freebsd.org/ftp/releases/VM-IMAGES/11.1-RELEASE/amd64/Latest/ | ||
97 | DL_SHA512="c569776334131fdc85cd25a2a0d5aecafdc3e4b2e6e010dffaa2488d934293ce4f091f23481079dd91ad20dfd2dfc3d3487707096c59448f1d8914c5d7d6b582" | ||
98 | # Selecting random mirror from https://www.freebsd.org/doc/handbook/mirrors-ftp.html | ||
99 | # Note that not all mirrors listed on that page are working, so we have removed them | ||
100 | # I'm so sorry, there are no arrays in sh and we are not using bash... | ||
101 | DL_MIRROR_1=1 | ||
102 | DL_MIRROR_2=4 | ||
103 | DL_MIRROR_3=5 | ||
104 | DL_MIRROR_4=6 | ||
105 | DL_MIRROR_5=8 | ||
106 | DL_MIRROR_6=10 | ||
107 | DL_MIRROR_7=14 | ||
108 | DL_MIRROR_8=15 | ||
109 | # There are 8 mirrors | ||
110 | DL_MIRROR_RANDOM=`expr $(date +%s) % 8 + 1` | ||
111 | DL_URL=ftp://ftp$(eval echo \$DL_MIRROR_$DL_MIRROR_RANDOM).us.freebsd.org/pub/FreeBSD/releases/VM-IMAGES/${FREEBSD_VERSION}-RELEASE/amd64/Latest/${IMAGE_NAME}.xz | ||
112 | |||
113 | wget $DL_URL | ||
114 | |||
115 | if ! ( echo "$DL_SHA512 $IMAGE_NAME.xz" | sha512sum -c --status - ) ; then | ||
116 | echo "Error: sha512 of $IMAGE_NAME.xz doesn't match the known one" | ||
117 | exit 1 | ||
118 | fi | ||
119 | |||
120 | unxz $IMAGE_NAME.xz | ||
121 | |||
122 | # With this we don't have to guess how long a command will run for and sleeping | ||
123 | # for that amount of time, risking either under sleeping or over sleeping, instead | ||
124 | # we will sleep exactly until the command is finished by printing out a unique | ||
125 | # string after the command is executed and then checking if it was printed. | ||
126 | execute_shell_and_wait() | ||
127 | { | ||
128 | # $RANDOM is a bash built-in, so we try to avoid name collision here by using ugly RANDOM_STR name | ||
129 | RANDOM_STR=$(< /dev/urandom tr -dc _A-Za-z0-9 | head -c16) | ||
130 | send_keys "$1;echo $RANDOM_STR | ||
131 | |||
132 | " | ||
133 | # \[1B is a control escape sequence for a new line in the terminal. | ||
134 | # We want to wait for <new-line>$RANDOM_STR instead of just $RANDOM_STR because | ||
135 | # $RANDOM_STR we have inputted with send_keys above would appear in the screenlog.0 | ||
136 | # file and we don't want to match our input, we want to match the echo's output. | ||
137 | # The .\? optionally matches any character. Sometimes it happens that there is some | ||
138 | # random character inserved between the new line control escape sequence and $RANDOM_STR. | ||
139 | wait_for "\[1B.\?$RANDOM_STR" | ||
140 | } | ||
141 | |||
142 | start_vm | ||
143 | |||
144 | # Login as root user | ||
145 | send_keys 'root | ||
146 | |||
147 | ' | ||
148 | |||
149 | # Wait for the prompt | ||
150 | wait_for "root@:~" | ||
151 | |||
152 | # Configure network, ssh and start changing password | ||
153 | execute_shell_and_wait 'echo "ifconfig_em0=DHCP" >> /etc/rc.conf' | ||
154 | execute_shell_and_wait 'echo "Port 22" >> /etc/ssh/sshd_config' | ||
155 | execute_shell_and_wait 'echo "PermitRootLogin yes" >> /etc/ssh/sshd_config' | ||
156 | execute_shell_and_wait 'echo "PasswordAuthentication yes" >> /etc/ssh/sshd_config' | ||
157 | execute_shell_and_wait 'echo "PermitEmptyPasswords yes" >> /etc/ssh/sshd_config' | ||
158 | execute_shell_and_wait 'echo "sshd_enable=YES" >> /etc/rc.conf' | ||
159 | send_keys 'sh /etc/rc.d/netif restart && sh /etc/rc.d/sshd start && passwd | ||
160 | ' | ||
161 | |||
162 | # Wait for the password prompt | ||
163 | wait_for "Changing local password for root" | ||
164 | |||
165 | # Reset password to empty for the passwordless ssh to work | ||
166 | send_keys ' | ||
167 | ' | ||
168 | wait_for "New Password" | ||
169 | send_keys ' | ||
170 | ' | ||
171 | |||
172 | # Update system | ||
173 | RUN env PAGER=cat env ASSUME_ALWAYS_YES=YES freebsd-update --not-running-from-cron fetch | ||
174 | # It fails if there is nothing to install, so we make it always succeed with true | ||
175 | RUN env PAGER=cat env ASSUME_ALWAYS_YES=YES freebsd-update --not-running-from-cron install || true | ||
176 | |||
177 | # Update packages | ||
178 | RUN env PAGER=cat env ASSUME_ALWAYS_YES=YES pkg upgrade | ||
179 | |||
180 | # Install and set bash as the default shell for the root user | ||
181 | RUN env PAGER=cat env ASSUME_ALWAYS_YES=YES pkg install bash | ||
182 | RUN chsh -s /usr/local/bin/bash root | ||
183 | |||
184 | # Install required toxcore dependencies | ||
185 | RUN PAGER=cat ASSUME_ALWAYS_YES=YES pkg install git \ | ||
186 | opus \ | ||
187 | libvpx \ | ||
188 | libsodium \ | ||
189 | gmake \ | ||
190 | cmake \ | ||
191 | pkgconf \ | ||
192 | check \ | ||
193 | opencv \ | ||
194 | portaudio \ | ||
195 | libsndfile \ | ||
196 | texinfo \ | ||
197 | autotools | ||
198 | |||
199 | # === Cache the VM image === | ||
200 | |||
201 | stop_vm | ||
202 | |||
203 | # Create cache | ||
204 | tar -Sczvf $IMAGE_NAME.tgz $IMAGE_NAME | ||
205 | rm screenlog.0 | ||
206 | |||
207 | cp "$OLD_PWD/GIT_TAGS" . | ||
208 | |||
209 | ls -lh | ||
210 | fi | ||
211 | |||
212 | if [ ! -f ./$IMAGE_NAME ]; then | ||
213 | # Extract the cached image | ||
214 | tar -Sxzvf $IMAGE_NAME.tgz | ||
215 | fi | ||
216 | |||
217 | # === Update the image on new version (tag) of toxcore === | ||
218 | if ! diff -u ./GIT_TAGS "$OLD_PWD/GIT_TAGS" ; then | ||
219 | start_vm | ||
220 | |||
221 | # Update system | ||
222 | RUN PAGER=cat ASSUME_ALWAYS_YES=YES freebsd-update --not-running-from-cron fetch | ||
223 | RUN PAGER=cat ASSUME_ALWAYS_YES=YES freebsd-update --not-running-from-cron install || true | ||
224 | |||
225 | # Update packages | ||
226 | RUN PAGER=cat ASSUME_ALWAYS_YES=YES pkg upgrade | ||
227 | |||
228 | # === Cache the updated VM image === | ||
229 | |||
230 | stop_vm | ||
231 | |||
232 | # Create/Update cache | ||
233 | rm $IMAGE_NAME.tgz | ||
234 | tar -Sczvf $IMAGE_NAME.tgz $IMAGE_NAME | ||
235 | rm screenlog.0 | ||
236 | |||
237 | cp "$OLD_PWD/GIT_TAGS" . | ||
238 | |||
239 | ls -lh | ||
240 | fi | ||
241 | |||
242 | # Get the image we will be using out of the cached directory | ||
243 | mv $IMAGE_NAME .. | ||
244 | ls -lh | ||
245 | |||
246 | cd .. | ||
247 | |||
248 | ls -lh | ||
249 | |||
250 | # === Get VM ready to build the code === | ||
251 | |||
252 | start_vm | ||
253 | |||
254 | # Display FreeBSD kernel info and last login | ||
255 | RUN uname -a | ||
256 | RUN last | ||
257 | |||
258 | cd "$OLD_PWD" | 87 | cd "$OLD_PWD" |
259 | |||
260 | # Copy over toxcore code from Travis to qemu | ||
261 | scp -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -P $SSH_PORT -r ./* root@localhost:~ | ||
262 | |||
263 | RUN ls -lh | ||
diff --git a/other/travis/freebsd-install-stage1 b/other/travis/freebsd-install-stage1 new file mode 100755 index 00000000..31544c44 --- /dev/null +++ b/other/travis/freebsd-install-stage1 | |||
@@ -0,0 +1,165 @@ | |||
1 | #!/bin/sh | ||
2 | |||
3 | git tag -l --sort=version:refname > GIT_TAGS | ||
4 | |||
5 | OLD_PWD="$PWD" | ||
6 | |||
7 | mkdir -p /opt/freebsd/cache | ||
8 | cd /opt/freebsd/cache | ||
9 | |||
10 | # === Get the VM image, set it up and cache === | ||
11 | |||
12 | # Create image if it's not cached or if this build script has changed | ||
13 | if [ ! -f ./$IMAGE_NAME.tgz ] || [ ! -f ./freebsd-install-stage1.sha256 ] || [ "`cat freebsd-install-stage1.sha256`" != "`sha256sum $OLD_PWD/other/travis/freebsd-install-stage1`" ]; then | ||
14 | |||
15 | rm -rf ./* | ||
16 | |||
17 | # https://download.freebsd.org/ftp/releases/VM-IMAGES/11.1-RELEASE/amd64/Latest/ | ||
18 | DL_SHA512="c569776334131fdc85cd25a2a0d5aecafdc3e4b2e6e010dffaa2488d934293ce4f091f23481079dd91ad20dfd2dfc3d3487707096c59448f1d8914c5d7d6b582" | ||
19 | # Selecting random mirror from https://www.freebsd.org/doc/handbook/mirrors-ftp.html | ||
20 | # Note that not all mirrors listed on that page are working, so we have removed them | ||
21 | # I'm so sorry, there are no arrays in sh and we are not using bash... | ||
22 | DL_MIRROR_1=1 | ||
23 | DL_MIRROR_2=4 | ||
24 | DL_MIRROR_3=5 | ||
25 | DL_MIRROR_4=6 | ||
26 | DL_MIRROR_5=8 | ||
27 | DL_MIRROR_6=10 | ||
28 | DL_MIRROR_7=14 | ||
29 | DL_MIRROR_8=15 | ||
30 | # There are 8 mirrors | ||
31 | DL_MIRROR_RANDOM=`expr $(date +%s) % 8 + 1` | ||
32 | DL_URL=ftp://ftp$(eval echo \$DL_MIRROR_$DL_MIRROR_RANDOM).us.freebsd.org/pub/FreeBSD/releases/VM-IMAGES/${FREEBSD_VERSION}-RELEASE/amd64/Latest/${IMAGE_NAME}.xz | ||
33 | |||
34 | wget $DL_URL | ||
35 | |||
36 | if ! ( echo "$DL_SHA512 $IMAGE_NAME.xz" | sha512sum -c --status - ) ; then | ||
37 | echo "Error: sha512 of $IMAGE_NAME.xz doesn't match the known one" | ||
38 | exit 1 | ||
39 | fi | ||
40 | |||
41 | unxz $IMAGE_NAME.xz | ||
42 | |||
43 | # With this we don't have to guess how long a command will run for and sleeping | ||
44 | # for that amount of time, risking either under sleeping or over sleeping, instead | ||
45 | # we will sleep exactly until the command is finished by printing out a unique | ||
46 | # string after the command is executed and then checking if it was printed. | ||
47 | execute_shell_and_wait() | ||
48 | { | ||
49 | # $RANDOM is a bash built-in, so we try to avoid name collision here by using ugly RANDOM_STR name | ||
50 | RANDOM_STR=$(< /dev/urandom tr -dc _A-Za-z0-9 | head -c16) | ||
51 | send_keys "$1;echo $RANDOM_STR | ||
52 | |||
53 | " | ||
54 | # \[1B is a control escape sequence for a new line in the terminal. | ||
55 | # We want to wait for <new-line>$RANDOM_STR instead of just $RANDOM_STR because | ||
56 | # $RANDOM_STR we have inputted with send_keys above would appear in the screenlog.0 | ||
57 | # file and we don't want to match our input, we want to match the echo's output. | ||
58 | # The .\? optionally matches any character. Sometimes it happens that there is some | ||
59 | # random character inserved between the new line control escape sequence and $RANDOM_STR. | ||
60 | wait_for "\[1B.\?$RANDOM_STR" | ||
61 | } | ||
62 | |||
63 | start_vm | ||
64 | |||
65 | # Login as root user | ||
66 | send_keys 'root | ||
67 | |||
68 | ' | ||
69 | |||
70 | # Wait for the prompt | ||
71 | wait_for "root@:~" | ||
72 | |||
73 | # Configure network, ssh and start changing password | ||
74 | execute_shell_and_wait 'echo "ifconfig_em0=DHCP" >> /etc/rc.conf' | ||
75 | execute_shell_and_wait 'echo "Port 22" >> /etc/ssh/sshd_config' | ||
76 | execute_shell_and_wait 'echo "PermitRootLogin yes" >> /etc/ssh/sshd_config' | ||
77 | execute_shell_and_wait 'echo "PasswordAuthentication yes" >> /etc/ssh/sshd_config' | ||
78 | execute_shell_and_wait 'echo "PermitEmptyPasswords yes" >> /etc/ssh/sshd_config' | ||
79 | execute_shell_and_wait 'echo "sshd_enable=YES" >> /etc/rc.conf' | ||
80 | send_keys 'sh /etc/rc.d/netif restart && sh /etc/rc.d/sshd start && passwd | ||
81 | ' | ||
82 | |||
83 | # Wait for the password prompt | ||
84 | wait_for "Changing local password for root" | ||
85 | |||
86 | # Reset password to empty for the passwordless ssh to work | ||
87 | send_keys ' | ||
88 | ' | ||
89 | wait_for "New Password" | ||
90 | send_keys ' | ||
91 | ' | ||
92 | |||
93 | # Update system | ||
94 | RUN env PAGER=cat env ASSUME_ALWAYS_YES=YES freebsd-update --not-running-from-cron fetch | ||
95 | # It fails if there is nothing to install, so we make it always succeed with true | ||
96 | RUN env PAGER=cat env ASSUME_ALWAYS_YES=YES freebsd-update --not-running-from-cron install || true | ||
97 | |||
98 | # Update packages | ||
99 | RUN env PAGER=cat env ASSUME_ALWAYS_YES=YES pkg upgrade | ||
100 | |||
101 | # Install and set bash as the default shell for the root user | ||
102 | RUN env PAGER=cat env ASSUME_ALWAYS_YES=YES pkg install bash | ||
103 | RUN chsh -s /usr/local/bin/bash root | ||
104 | |||
105 | # Install required toxcore dependencies | ||
106 | RUN PAGER=cat ASSUME_ALWAYS_YES=YES pkg install git \ | ||
107 | opus \ | ||
108 | libvpx \ | ||
109 | libsodium \ | ||
110 | gmake \ | ||
111 | cmake \ | ||
112 | pkgconf \ | ||
113 | check \ | ||
114 | opencv \ | ||
115 | portaudio \ | ||
116 | libsndfile \ | ||
117 | texinfo \ | ||
118 | autotools | ||
119 | |||
120 | # === Cache the VM image === | ||
121 | |||
122 | stop_vm | ||
123 | |||
124 | # Create cache | ||
125 | tar -Sczvf $IMAGE_NAME.tgz $IMAGE_NAME | ||
126 | rm $IMAGE_NAME | ||
127 | rm screenlog.0 | ||
128 | |||
129 | cp "$OLD_PWD/GIT_TAGS" . | ||
130 | sha256sum "$OLD_PWD/other/travis/freebsd-install-stage1" > freebsd-install-stage1.sha256 | ||
131 | |||
132 | ls -lh | ||
133 | fi | ||
134 | |||
135 | # === Update the image on new version (tag) of toxcore === | ||
136 | |||
137 | if ! diff -u ./GIT_TAGS "$OLD_PWD/GIT_TAGS" ; then | ||
138 | # Extract the cached image | ||
139 | tar -Sxzvf $IMAGE_NAME.tgz | ||
140 | |||
141 | start_vm | ||
142 | |||
143 | # Update system | ||
144 | RUN PAGER=cat ASSUME_ALWAYS_YES=YES freebsd-update --not-running-from-cron fetch | ||
145 | RUN PAGER=cat ASSUME_ALWAYS_YES=YES freebsd-update --not-running-from-cron install || true | ||
146 | |||
147 | # Update packages | ||
148 | RUN PAGER=cat ASSUME_ALWAYS_YES=YES pkg upgrade | ||
149 | |||
150 | # === Cache the updated VM image === | ||
151 | |||
152 | stop_vm | ||
153 | |||
154 | # Create/Update cache | ||
155 | rm $IMAGE_NAME.tgz | ||
156 | tar -Sczvf $IMAGE_NAME.tgz $IMAGE_NAME | ||
157 | rm $IMAGE_NAME | ||
158 | rm screenlog.0 | ||
159 | |||
160 | cp "$OLD_PWD/GIT_TAGS" . | ||
161 | |||
162 | ls -lh | ||
163 | fi | ||
164 | |||
165 | cd "$OLD_PWD" | ||
diff --git a/other/travis/freebsd-install-stage2 b/other/travis/freebsd-install-stage2 new file mode 100755 index 00000000..61f8d013 --- /dev/null +++ b/other/travis/freebsd-install-stage2 | |||
@@ -0,0 +1,32 @@ | |||
1 | #!/bin/sh | ||
2 | |||
3 | OLD_PWD="$PWD" | ||
4 | |||
5 | mkdir -p /opt/freebsd/cache | ||
6 | cd /opt/freebsd/cache | ||
7 | |||
8 | # Extract the cached image | ||
9 | tar -Sxzvf $IMAGE_NAME.tgz | ||
10 | |||
11 | # Get the image we will be using out of the cached directory | ||
12 | mv $IMAGE_NAME .. | ||
13 | ls -lh | ||
14 | |||
15 | cd .. | ||
16 | |||
17 | ls -lh | ||
18 | |||
19 | # === Get VM ready to build the code === | ||
20 | |||
21 | start_vm | ||
22 | |||
23 | # Display FreeBSD kernel info and last login | ||
24 | RUN uname -a | ||
25 | RUN last | ||
26 | |||
27 | cd "$OLD_PWD" | ||
28 | |||
29 | # Copy over toxcore code from Travis to qemu | ||
30 | scp -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -P $SSH_PORT -r ./* root@localhost:~ | ||
31 | |||
32 | RUN ls -lh | ||
diff --git a/other/travis/freebsd-script-stage1 b/other/travis/freebsd-script-stage1 new file mode 100755 index 00000000..ce16aea1 --- /dev/null +++ b/other/travis/freebsd-script-stage1 | |||
@@ -0,0 +1,4 @@ | |||
1 | #!/bin/sh | ||
2 | |||
3 | # We don't want toxcore-script to run yet, it should be done during FreeBSD stage 2 | ||
4 | exit 0 | ||
diff --git a/other/travis/phase b/other/travis/phase index ac9bc3b4..cb9c2abf 100755 --- a/other/travis/phase +++ b/other/travis/phase | |||
@@ -5,6 +5,13 @@ set -e -u -x | |||
5 | JOB="$1" | 5 | JOB="$1" |
6 | ENV="$2" | 6 | ENV="$2" |
7 | PHASE="$3" | 7 | PHASE="$3" |
8 | STAGE="" | ||
9 | |||
10 | set +u | ||
11 | if [ ! -z "$4" ] ; then | ||
12 | STAGE="$4" | ||
13 | fi | ||
14 | set -u | ||
8 | 15 | ||
9 | . "other/travis/env.sh" | 16 | . "other/travis/env.sh" |
10 | . "other/travis/env-$ENV.sh" | 17 | . "other/travis/env-$ENV.sh" |
@@ -14,6 +21,9 @@ try_source() { | |||
14 | if [ -f "$SCRIPT" ]; then | 21 | if [ -f "$SCRIPT" ]; then |
15 | . "$SCRIPT" | 22 | . "$SCRIPT" |
16 | fi | 23 | fi |
24 | if [ ! -z "$STAGE" ] && [ -f "$SCRIPT-$STAGE" ]; then | ||
25 | . "$SCRIPT-$STAGE" | ||
26 | fi | ||
17 | } | 27 | } |
18 | 28 | ||
19 | try_source "$PHASE" | 29 | try_source "$PHASE" |