diff --git a/app/Go.java b/app/Go.java
deleted file mode 100644
index ece8c4c..0000000
--- a/app/Go.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package go;
-
-import android.content.Context;
-import android.os.Looper;
-import android.util.Log;
-
-// Go is an entry point for libraries compiled in Go.
-// In an app's Application.onCreate, call:
-//
-// Go.init(getApplicationContext());
-//
-// When the function returns, it is safe to start calling
-// Go code.
-public final class Go {
- // init loads libgojni.so and starts the runtime.
- public static void init(final Context ctx) {
- if (Looper.myLooper() != Looper.getMainLooper()) {
- Log.wtf("Go", "Go.init must be called from main thread (looper="+Looper.myLooper().toString()+")");
- }
- if (running) {
- return;
- }
- running = true;
-
- // TODO(crawshaw): context.registerComponentCallbacks for runtime.GC
-
- System.loadLibrary("gojni");
- Go.run(ctx);
- new Thread("GoReceive") {
- public void run() { Seq.receive(); }
- }.start();
- }
-
- private static boolean running = false;
-
- private static native void run(Context ctx);
-}
diff --git a/app/android.c b/app/android.c
index 08f350f..7f6b206 100644
--- a/app/android.c
+++ b/app/android.c
@@ -45,7 +45,6 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
current_vm = vm;
current_ctx = NULL;
- current_native_activity = NULL;
return JNI_VERSION_1_6;
}
@@ -121,15 +120,15 @@ static void* call_main_and_wait() {
callMain(mainPC);
}
-// Runtime entry point when using NativeActivity.
+// Entry point from NativeActivity.
+//
+// By here, the Go runtime has been initialized (as we are running in
+// -buildmode=c-shared) but main.main hasn't been called yet.
void ANativeActivity_onCreate(ANativeActivity *activity, void* savedState, size_t savedStateSize) {
- current_native_activity = activity;
- if (current_ctx == NULL) {
- // Note that activity->clazz is mis-named.
- current_vm = activity->vm;
- current_ctx = (*activity->env)->NewGlobalRef(activity->env, activity->clazz);
- call_main_and_wait();
- }
+ // Note that activity->clazz is mis-named.
+ current_vm = activity->vm;
+ current_ctx = (*activity->env)->NewGlobalRef(activity->env, activity->clazz);
+ call_main_and_wait();
// These functions match the methods on Activity, described at
// http://developer.android.com/reference/android/app/Activity.html
@@ -153,23 +152,3 @@ void ANativeActivity_onCreate(ANativeActivity *activity, void* savedState, size_
onCreate(activity);
}
-
-// Runtime entry point when embedding Go in a Java App.
-JNIEXPORT void JNICALL
-Java_go_Go_run(JNIEnv* env, jclass clazz, jobject ctx) {
- current_ctx = (*env)->NewGlobalRef(env, ctx);
-
- if (current_ctx != NULL) {
- // Init asset_manager.
- jclass context_clazz = find_class(env, "android/content/Context");
- jmethodID getassets = find_method(
- env, context_clazz, "getAssets", "()Landroid/content/res/AssetManager;");
-
- // Prevent the java AssetManager from being GC'd
- jobject asset_manager_ref = (*env)->NewGlobalRef(
- env, (*env)->CallObjectMethod(env, current_ctx, getassets));
- asset_manager = AAssetManager_fromJava(env, asset_manager_ref);
- }
-
- call_main_and_wait();
-}
diff --git a/app/android.go b/app/android.go
index 40217f1..90b6f33 100644
--- a/app/android.go
+++ b/app/android.go
@@ -14,11 +14,9 @@ until this is complete. Next JNI_OnLoad is called. When that is
complete, one of two entry points is called.
All-Go apps built using NativeActivity enter at ANativeActivity_onCreate.
-Go libraries, such as those built with gomobild bind, enter from Java at
-Java_go_Go_run.
-Both entry points make a cgo call that calls the Go main and blocks
-until app.Run is called.
+Go libraries (for example, those built with gomobile bind) do not use
+the app package initialization.
*/
package app
@@ -49,9 +47,6 @@ jobject current_ctx;
jclass app_find_class(JNIEnv* env, const char* name);
-// current_native_activity is the Android ANativeActivity. May be NULL.
-ANativeActivity* current_native_activity;
-
// asset_manager is the asset manager of the app.
// For all-Go app, this is initialized in onCreate.
// For go library app, this is set from the context passed to Go.run.
@@ -291,10 +286,6 @@ func main(f func(App)) {
close(donec)
}()
- if C.current_native_activity == nil {
- <-donec
- return
- }
for w := range windowCreated {
if windowDraw(w, queue, donec) {
return
diff --git a/bind/java/Seq.java b/bind/java/Seq.java
index 7c0b4a8..f71eb37 100644
--- a/bind/java/Seq.java
+++ b/bind/java/Seq.java
@@ -14,6 +14,13 @@ import java.util.concurrent.Executors;
// Seq is a sequence of machine-dependent encoded values.
// Used by automatically generated language bindings to talk to Go.
public class Seq {
+ static {
+ initSeq();
+ new Thread("GoSeq") {
+ public void run() { Seq.receive(); }
+ }.start();
+ }
+
@SuppressWarnings("UnusedDeclaration")
private long memptr; // holds C-allocated pointer
@@ -67,10 +74,6 @@ public class Seq {
return tracker.get(refnum);
}
- static {
- initSeq();
- }
-
static native void initSeq();
// Informs the Go ref tracker that Java is done with this ref.
diff --git a/bind/java/SeqTest.java b/bind/java/SeqTest.java
index 27bef2b..ff8a724 100644
--- a/bind/java/SeqTest.java
+++ b/bind/java/SeqTest.java
@@ -15,7 +15,7 @@ import go.testpkg.Testpkg;
public class SeqTest extends AndroidTestCase {
public SeqTest() {
- Go.init(this.getContext());
+ System.loadLibrary("gojni");
}
public void testAdd() {
diff --git a/bind/java/doc.go b/bind/java/doc.go
index b846381..ee85203 100644
--- a/bind/java/doc.go
+++ b/bind/java/doc.go
@@ -8,11 +8,3 @@
//
// Currently, this works only for android.
package java
-
-// Init initializes communication with Java.
-// Typically called from the Start callback in app.Run.
-func Init() {
- close(running)
-}
-
-var running = make(chan struct{})
diff --git a/bind/java/seq_android.go b/bind/java/seq_android.go
index 6b0145b..e24d360 100644
--- a/bind/java/seq_android.go
+++ b/bind/java/seq_android.go
@@ -25,7 +25,6 @@ const debug = false
// Send is called by Java to send a request to run a Go function.
//export Send
func Send(descriptor string, code int, req *C.uint8_t, reqlen C.size_t, res **C.uint8_t, reslen *C.size_t) {
- <-running
fn := seq.Registry[descriptor][code]
if fn == nil {
panic(fmt.Sprintf("invalid descriptor(%s) and code(0x%x)", descriptor, code))
diff --git a/cmd/gomobile/bind.go b/cmd/gomobile/bind.go
index 127f59c..82246b8 100644
--- a/cmd/gomobile/bind.go
+++ b/cmd/gomobile/bind.go
@@ -150,15 +150,8 @@ func runBind(cmd *command) error {
return err
}
- src := filepath.Join(repo, "app/Go.java")
- dst := filepath.Join(androidDir, "src/main/java/go/Go.java")
- rm(dst)
- if err := symlink(src, dst); err != nil {
- return err
- }
-
- src = filepath.Join(repo, "bind/java/Seq.java")
- dst = filepath.Join(androidDir, "src/main/java/go/Seq.java")
+ src := filepath.Join(repo, "bind/java/Seq.java")
+ dst := filepath.Join(androidDir, "src/main/java/go/Seq.java")
rm(dst)
if err := symlink(src, dst); err != nil {
return err
@@ -292,15 +285,11 @@ var androidMainTmpl = template.Must(template.New("android.go").Parse(`
package main
import (
- "golang.org/x/mobile/app"
- "golang.org/x/mobile/bind/java"
-
+ _ "golang.org/x/mobile/bind/java"
_ "{{.}}"
)
-func main() {
- app.Run(app.Callbacks{Start: java.Init})
-}
+func main() {}
`))
// AAR is the format for the binary distribution of an Android Library Project
diff --git a/example/libhello/AndroidManifest.xml b/example/libhello/AndroidManifest.xml
deleted file mode 100644
index dddb380..0000000
--- a/example/libhello/AndroidManifest.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/example/libhello/README b/example/libhello/README
deleted file mode 100644
index ce7f17b..0000000
--- a/example/libhello/README
+++ /dev/null
@@ -1,42 +0,0 @@
-The libhello app demonstrates calling Go code from a primarily Java app.
-
-Starting in Java lets you program against Android's extensive UI
-libraries in their native language and call into Go for library code
-(business logic, code shared with a Go server, portable code).
-
-The Java entry point to the program is the file
-src/com/example/hello/MainActivity.java, where the statement
-
- Hi.Hello("world");
-
-is a call into Go code.
-
-The Go code is in a package called hi, the file is hi/hi.go, and it
-contains the function Hello:
-
- func Hello(name string) {
- fmt.Printf("Hello, %s!\n", name)
- }
-
-Java language bindings are generated for this package using the gobind
-tool. There is a user guide for gobind at
-
- http://golang.org/x/mobile/cmd/gobind
-
-The generated source has been included in the distribution. If you
-modify the exported interface of package hi, you have to run gobind
-manually before calling all.bash.
-
-Along with the gobind generated source, the app includes a main.go file
-to define the app entry point.
-
-make.bash builds the app, all.bash deploys it.
-
-The first step in building the app is to build the native shared
-library out of the Go code, and place it in
-libs/armeabi-v7a/libgojni.so.
-
-The second step is building the app with the standard Android build
-system by calling ant debug (also done in make.bash). Two extra Java
-files are included in the build by make.bash to support the language
-bindings. This produces an apk ready for running on a device.
diff --git a/example/libhello/all.bash b/example/libhello/all.bash
deleted file mode 100755
index be7052f..0000000
--- a/example/libhello/all.bash
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/usr/bin/env bash
-# Copyright 2014 The Go Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-
-# Script to build and launch the app on an android device.
-
-set -e
-
-./make.bash
-
-adb install -r bin/Hello-debug.apk
-
-adb shell am start -a android.intent.action.MAIN \
- -n com.example.hello/com.example.hello.MainActivity
diff --git a/example/libhello/all.bat b/example/libhello/all.bat
deleted file mode 100644
index 5ecebe4..0000000
--- a/example/libhello/all.bat
+++ /dev/null
@@ -1,16 +0,0 @@
-:: Copyright 2014 The Go Authors. All rights reserved.
-:: Use of this source code is governed by a BSD-style
-:: license that can be found in the LICENSE file.
-
-@echo off
-
-setlocal
-
-echo # building libhello
-call make.bat
-
-echo # installing bin/Hello-debug.apk
-adb install -r bin/Hello-debug.apk >nul
-
-echo # starting com.example.hello.MainActivity
-adb shell am start -a android.intent.action.MAIN -n com.example.hello/com.example.hello.MainActivity >nul
\ No newline at end of file
diff --git a/example/libhello/build.xml b/example/libhello/build.xml
deleted file mode 100644
index 066f914..0000000
--- a/example/libhello/build.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/example/libhello/hi/go_hi/go_hi.go b/example/libhello/hi/go_hi/go_hi.go
deleted file mode 100644
index 89b9d2d..0000000
--- a/example/libhello/hi/go_hi/go_hi.go
+++ /dev/null
@@ -1,19 +0,0 @@
-// Package go_hi is an autogenerated binder stub for package hi.
-// gobind -lang=go golang.org/x/mobile/example/libhello/hi
-//
-// File is generated by gobind. Do not edit.
-package go_hi
-
-import (
- "golang.org/x/mobile/bind/seq"
- "golang.org/x/mobile/example/libhello/hi"
-)
-
-func proxy_Hello(out, in *seq.Buffer) {
- param_name := in.ReadUTF16()
- hi.Hello(param_name)
-}
-
-func init() {
- seq.Register("hi", 1, proxy_Hello)
-}
diff --git a/example/libhello/hi/hi.go b/example/libhello/hi/hi.go
deleted file mode 100644
index a1e868b..0000000
--- a/example/libhello/hi/hi.go
+++ /dev/null
@@ -1,8 +0,0 @@
-// Package hi provides a function for saying hello.
-package hi
-
-import "fmt"
-
-func Hello(name string) {
- fmt.Printf("Hello, %s!\n", name)
-}
diff --git a/example/libhello/main.go b/example/libhello/main.go
deleted file mode 100644
index 320529a..0000000
--- a/example/libhello/main.go
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This is the Go entry point for the libhello app.
-// It is invoked from Java.
-//
-// See README for details.
-package main
-
-import (
- "golang.org/x/mobile/app"
-
- "golang.org/x/mobile/bind/java"
- _ "golang.org/x/mobile/example/libhello/hi/go_hi"
-)
-
-func main() {
- app.Run(app.Callbacks{Start: java.Init})
-}
diff --git a/example/libhello/make.bash b/example/libhello/make.bash
deleted file mode 100755
index 97bd45d..0000000
--- a/example/libhello/make.bash
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/usr/bin/env bash
-# Copyright 2014 The Go Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-
-set -e
-
-if [ ! -f make.bash ]; then
- echo 'make.bash must be run from $GOPATH/src/golang.org/x/mobile/example/libhello'
- exit 1
-fi
-
-mkdir -p libs/armeabi-v7a src/go/hi
-ANDROID_APP=$PWD
-(cd ../.. && ln -sf $PWD/app/*.java $ANDROID_APP/src/go)
-(cd ../.. && ln -sf $PWD/bind/java/Seq.java $ANDROID_APP/src/go)
-CGO_ENABLED=1 GOOS=android GOARCH=arm GOARM=7 \
- go build -ldflags="-shared" .
-mv -f libhello libs/armeabi-v7a/libgojni.so
-ant debug
diff --git a/example/libhello/make.bat b/example/libhello/make.bat
deleted file mode 100644
index 1955ade..0000000
--- a/example/libhello/make.bat
+++ /dev/null
@@ -1,46 +0,0 @@
-:: Copyright 2014 The Go Authors. All rights reserved.
-:: Use of this source code is governed by a BSD-style
-:: license that can be found in the LICENSE file.
-
-@echo off
-
-setlocal
-
-if not exist make.bat goto error-invalid-path
-
-:go-build
-if not exist libs\armeabi-v7a mkdir libs\armeabi-v7a
-if not exist src\go\hi mkdir src\go\hi
-if not exist jni\armeabi mkdir jni\armeabi
-
-set CGO_ENABLED=1
-set GOOS=android
-set GOARCH=arm
-set GOARM=7
-set ANDROID_APP=%CD%
-
-xcopy /y ..\..\app\*.java %ANDROID_APP%\src\go >nul
-copy /y ..\..\bind\java\Seq.java %ANDROID_APP%\src\go\Seq.java >nul
-
-go build -ldflags="-shared" .
-if errorlevel 1 goto error-go-build
-
-move /y libhello libs\armeabi-v7a\libgojni.so >nul
-
-if defined ANT_HOME goto ant-build
-echo ANT_HOME path not defined
-goto end
-
-:ant-build
-call %ANT_HOME%\bin\ant.bat debug >nul
-goto end
-
-:error-invalid-path
-echo make.bat must be run from example\libhello
-goto end
-
-:error-go-build
-echo Error building go lib
-goto end
-
-:end
\ No newline at end of file
diff --git a/example/libhello/src/com/example/hello/MainActivity.java b/example/libhello/src/com/example/hello/MainActivity.java
deleted file mode 100644
index 61fc8b6..0000000
--- a/example/libhello/src/com/example/hello/MainActivity.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2014 The Go Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style
- * license that can be found in the LICENSE file.
- */
-
-package com.example.hello;
-
-import go.Go;
-import go.hi.Hi;
-import android.app.Activity;
-import android.os.Bundle;
-
-/*
- * MainActivity is the entry point for the libhello app.
- *
- * From here, the Go runtime is initialized and a Go function is
- * invoked via gobind language bindings.
- *
- * See example/libhello/README for details.
- */
-public class MainActivity extends Activity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- Go.init(getApplicationContext());
- Hi.Hello("world");
- }
-}
diff --git a/example/libhello/src/go/hi/Hi.java b/example/libhello/src/go/hi/Hi.java
deleted file mode 100644
index 6514f5a..0000000
--- a/example/libhello/src/go/hi/Hi.java
+++ /dev/null
@@ -1,20 +0,0 @@
-// Java Package hi is a proxy for talking to a Go program.
-//
-// File is generated by gobind. Do not edit.
-package go.hi;
-
-import go.Seq;
-
-public abstract class Hi {
- private Hi() {} // uninstantiable
-
- public static void Hello(String name) {
- go.Seq _in = new go.Seq();
- go.Seq _out = new go.Seq();
- _in.writeUTF16(name);
- Seq.send(DESCRIPTOR, CALL_Hello, _in, _out);
- }
-
- private static final int CALL_Hello = 1;
- private static final String DESCRIPTOR = "hi";
-}
diff --git a/example/libhellojni/Demo.java b/example/libhellojni/Demo.java
deleted file mode 100644
index bfbceb5..0000000
--- a/example/libhellojni/Demo.java
+++ /dev/null
@@ -1,9 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package demo;
-
-public class Demo {
- public static native void hello(String name);
-}
diff --git a/example/libhellojni/hello.c b/example/libhellojni/hello.c
deleted file mode 100644
index d96b405..0000000
--- a/example/libhellojni/hello.c
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build android
-
-// See main.go for commentary.
-
-#include
-#include
-#include
-#include "_cgo_export.h"
-
-JNIEXPORT void JNICALL
-Java_demo_Demo_hello(JNIEnv* env, jclass clazz, jstring jname) {
- // Turn Java's UTF16 string into (almost) UTF8.
- const char *name = (*env)->GetStringUTFChars(env, jname, 0);
-
- GoString go_name;
- go_name.p = (char*)name;
- go_name.n = (*env)->GetStringUTFLength(env, jname);
-
- // Call into Go.
- LogHello(go_name);
-
- (*env)->ReleaseStringUTFChars(env, jname, name);
-}
diff --git a/example/libhellojni/hello.go b/example/libhellojni/hello.go
deleted file mode 100644
index 666666d..0000000
--- a/example/libhellojni/hello.go
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build android
-
-package main
-
-// #cgo LDFLAGS: -llog
-// #include
-// #include
-import "C"
-import (
- "fmt"
- "unsafe"
-)
-
-//export LogHello
-func LogHello(name string) {
- fmt.Printf("Hello, %s!\n", name)
-
- ctag := C.CString("Go")
- cstr := C.CString(fmt.Sprintf("Printing hello message for %q", name))
- C.__android_log_write(C.ANDROID_LOG_INFO, ctag, cstr)
- C.free(unsafe.Pointer(ctag))
- C.free(unsafe.Pointer(cstr))
-}
diff --git a/example/libhellojni/main.go b/example/libhellojni/main.go
deleted file mode 100644
index 79a16b4..0000000
--- a/example/libhellojni/main.go
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-/*
-This example program compiles to a gojni.so shared library, that can
-be loaded from an android application. Build it by configuring a cross
-compiler (see go.mobile/README) and then running:
-
-ANDROID_APP=/path/to/Myapp/app ./make.bash
-
-This program expects app/Go.java to be included in the Android
-project, along with a Java class named Demo defining
-
- public static native void hello();
-
-calling hello prints "Hello, world!" to logcat.
-
-This is a very early example program that does not represent the
-intended development model for Go on Android. A language binding
-generator will follow, as will gradle build system integration.
-The result will be no make.bash, and no need to write C.
-*/
-package main
-
-import "golang.org/x/mobile/app"
-
-func main() {
- app.Run(app.Callbacks{})
-}
diff --git a/example/libhellojni/make.bash b/example/libhellojni/make.bash
deleted file mode 100755
index 1938151..0000000
--- a/example/libhellojni/make.bash
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/usr/bin/env bash
-# Copyright 2014 The Go Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-
-# See main.go for commentary.
-
-set -e
-
-if [ ! -f make.bash ]; then
- echo 'make.bash must be run from $GOPATH/src/golang.org/x/mobile/example/libhellojni'
- exit 1
-fi
-if [ -z "$ANDROID_APP" ]; then
- echo 'ERROR: Environment variable ANDROID_APP is unset.'
- exit 1
-fi
-
-mkdir -p $ANDROID_APP/src/main/jniLibs/armeabi \
- $ANDROID_APP/src/main/java/go \
- $ANDROID_APP/src/main/java/demo
-(cd ../.. && ln -sf $PWD/app/*.java $ANDROID_APP/src/main/java/go)
-(cd ../.. && ln -sf $PWD/bind/java/*.java $ANDROID_APP/src/main/java/go)
-ln -sf $PWD/*.java $ANDROID_APP/src/main/java/demo
-CGO_ENABLED=1 GOOS=android GOARCH=arm GOARM=7 \
- go build -ldflags="-shared" .
-mv libhellojni $ANDROID_APP/src/main/jniLibs/armeabi/libgojni.so
diff --git a/example/libhellojni/make.bat b/example/libhellojni/make.bat
deleted file mode 100644
index 111e19b..0000000
--- a/example/libhellojni/make.bat
+++ /dev/null
@@ -1,39 +0,0 @@
-:: Copyright 2014 The Go Authors. All rights reserved.
-:: Use of this source code is governed by a BSD-style
-:: license that can be found in the LICENSE file.
-
-@echo off
-
-setlocal
-
-if not exist make.bat goto error-invalid-path
-
-set CGO_ENABLED=1
-set GOOS=android
-set GOARCH=arm
-set GOARM=7
-set ANDROID_APP=%CD%
-
-if not exist src\main\jniLibs\armeabi mkdir src\main\jniLibs\armeabi
-if not exist src\main\java\go mkdir src\main\java\go
-if not exist src\main\java\demo mkdir src\main\java\demo
-
-xcopy /y ..\..\app\*.java %ANDROID_APP%\src\main\java\go >nul
-xcopy /y ..\..\bind\java\*.java %ANDROID_APP%\src\main\java\go >nul
-xcopy /y %CD%\*.java %ANDROID_APP%\src\main\java\demo >nul
-
-go build -ldflags="-shared" .
-if errorlevel 1 goto error-go-build
-
-move /y libhellojni %ANDROID_APP%\src\main\jniLibs\armeabi\libgojni.so >nul
-goto end
-
-:error-invalid-path
-echo make.bat must be run from example\libhellojni
-goto end
-
-:error-go-build
-echo Error building go lib
-goto end
-
-:end
\ No newline at end of file